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 fromXmlSerializer
. Objects are not assigned to a namespace by default byXmlSerializer
, so your original model works as-is with that serializer. Demo fiddle #2 here.If you would prefer to use
XmlSerializer
instead ofDataContractSerializer
I believe you could removeAddXmlDataContractSerializerFormatters()
leaving onlyAddXmlSerializerFormatters()
.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 ofObject2
andObject3
is wrong. (The precise namespace chosen, herehttp://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"
orz:ref="xxx"
attributes, so I don't see any need to enable theIsReference
reference tracking mechanism.