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
}
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.