Home > Mobile >  .NET Core 3.1 API to Accept Complex XML Object
.NET Core 3.1 API to Accept Complex XML Object

Time:10-10


EDIT

XML Input would look like this

<?xml version="1.0" encoding="UTF-8"?>
<Object1>
   <field1>Hello</field1>
   <field2>
      <field3>World</field3>
      <field4>
         <field5>Test</field5>
         <field6>Test2</field6>
      </field4>
   </field2>
</Object1>

I am a bit stuck on how to get my API to accept a complex XML object

For example, I have a class that is like this

public class Object1
{
 public string field1 {get; set;}
 public Object2 field2 {get; set;}
}

public class Object2
{
 public string field3 {get; set;}
 public Object3 field4 {get; set;}
}

public class Object3
{
 public string field5 {get; set;}
 public string field6 {get; set;}
}

In my startup.cs file, I added this

services.AddControllers().AddXmlDataContractSerializerFormatters()
               .AddXmlSerializerFormatters();

I then got the error that it could not deserialize the input.

So I added these Attributes to the object

[DataContract(Namespace = "")]
[XmlRoot]
public class Object1
{
 [DataMember(Name = "field1")]
 public string field1 {get; set;}
 [DataMember(Name = "field2")]
 public Object2 field2 {get; set;}
}

[DataContract(Name = "field2", IsReference = true)]
public class Object2
{
 [DataMember(Name = "field3")]
 public string field3 {get; set;}
 [DataMember(Name = "field4")]
 public Object3 field4 {get; set;}
}

[DataContract(Name = "field3", IsReference = true)]
public class Object3
{
 [DataMember(Name = "field5")]
 public string field5 {get; set;}
 [DataMember(Name = "field6")]
 public string field6 {get; set;}
}

So now when I send the XML object over, it is now able to be somewhat worked on. I can get the field1 value and it looks like the field2 object is no longer null, but everything inside of it is null.

I am not sure how to approach this properly. Anything I am missing?

My Controller looks like this

[HttpPost]
[Consumes("application/xml")]
public async Task<IActionResult> UpdateObject([FromBody]Object1 object1)
{
  var testObject1 = object1.field1; // this value is not null because it is a string datatype and not like the one below which is slightly more complex
  var testObject2 = object1.field2; //this is not null but the properties inside the object are null
}

CodePudding user response:

You need set the DataContractAttribute.Namespace for Object2 and Object3 as well:

[DataContract(Namespace = "", Name = "field2", IsReference = true)]
public class Object2
{
 [DataMember(Name = "field3")]
 public string field3 {get; set;}
 [DataMember(Name = "field4")]
 public Object3 field4 {get; set;}
}

[DataContract(Namespace = "", Name = "field3", IsReference = true)]
public class Object3
{
 [DataMember(Name = "field5")]
 public string field5 {get; set;}
 [DataMember(Name = "field6")]
 public string field6 {get; set;}
}

Demo fiddle #1 here.

Notes:

  • With the data contract serializer, if you do not explicitly specify a namespace for a data contract object, a default is assigned as explained in the docs:

    By default, data contracts for a particular type are assigned a namespace that comes from the common language runtime (CLR) namespace of that type.

    By default, any given CLR namespace (in the format Clr.Namespace) is mapped to the namespace http://schemas.datacontract.org/2004/07/Clr.Namespace.

  • The default namespace logic of DataContractSerializer differs from XmlSerializer. Objects are not assigned to a namespace by default by XmlSerializer, so your original model works as-is with that serializer. Demo fiddle #2 here.

    If you would prefer to use XmlSerializer instead of DataContractSerializer I believe you could remove AddXmlDataContractSerializerFormatters() leaving only AddXmlSerializerFormatters().

  • An easy way to debug problems with deserialization is to serialize your model and compare the actual results with content you are trying to deserialize. If I attempt to serialize an instance of your current Object1 model, I get:

    <?xml version="1.0" encoding="utf-16"?>
    <Object1 xmlns:i="http://www.w3.org/2001/XMLSchema-instance">
      <field1>Hello</field1>
      <field2 xmlns:d2p1="http://schemas.datacontract.org/2004/07/" z:Id="i1" xmlns:z="http://schemas.microsoft.com/2003/10/Serialization/">
        <d2p1:field3>World</d2p1:field3>
        <d2p1:field4 z:Id="i2">
          <d2p1:field5>Test</d2p1:field5>
          <d2p1:field6>Test2</d2p1:field6>
        </d2p1:field4>
      </field2>
    </Object1>    
    

    From which it can be seen that the namespace d2p1: for the elements of Object2 and Object3 is wrong. (The precise namespace chosen, here http://schemas.datacontract.org/2004/07/, will depend on the CLR namespace of your model, which is not shown in your question.)

    Demo fiddle #3 here.

  • Your XML elements do not have z:Id="xxx" or z:ref="xxx" attributes, so I don't see any need to enable the IsReference reference tracking mechanism.

  • Related