Reputation: 839
Any help is appreciated with this, I've been struggling.
From a HTTP request I receive an XML response. I have no control over its structure, so I must match that structure with my data contracts. However, no matter what i do, all the properties in the Result object are either null or the default value.
XML Structure:
<Customnamedroot xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" Results="2" Skip="0">
<Object>
<Something xsi:nil="true" />
<Anything>2012-06-08T11:21:27</Anything>
<Booleanthing>true</Booleanthing>
</Object>
<Object>
<Something>1463387</Something>
<Anything>2012-06-07T09:16:11.41</Anything>
<Booleanthing>true</Booleanthing>
</Object>
</Customnamedroot>
The call:
SearchResults objects = response.Content.ReadAsAsync<SearchResults>().Result;
Corresponding Data Contract classes:
[CollectionDataContract(Name = "Customnamedroot", ItemName = "Object", Namespace = "")]
public class SearchResults : List<Result>
{
}
//[DataContract(Name = "Object")]
public class Result
{
[DataMember(Order = 1, Name = "Something", IsRequired = false)]
public decimal? Something{ get; set; }
[DataMember(Order = 2, Name = "Anything", IsRequired = true, EmitDefaultValue = false)]
public DateTime Anything{ get; set; }
[DataMember(Order = 3, Name = "Booleanthing", IsRequired = true, EmitDefaultValue = false)]
public bool Booleanthing{ get; set; }
}
Edit: This problem occurs if the DataContract(Name = "Object") is omitted. If that DataContractAttribute is added, I get the following error:
System.Runtime.Serialization.SerializationException: Error in line 1 position 157.
'EndElement' 'Object' from namespace '' is not expected.
Expecting element 'Something| Anything'.
What am I doing wrong?
Upvotes: 3
Views: 2012
Reputation: 4328
Remove ItemName
from the CollectionDataContract. Re-add the DataContract for the Result
class and add the Namespace
property to it with an empty string. If there is no namespace defined explicitly in the xml structure .Net will assume an empty string as the namespace and you must tie yourself to that.
[CollectionDataContract(Name = "Customnamedroot", Namespace = "")]
public class SearchResults : List<Result>
{
}
[DataContract(Name = "Object", Namespace = "")]
public class Result
{
[DataMember(Order = 1, Name = "Something", IsRequired = false)]
public decimal? Something{ get; set; }
[DataMember(Order = 2, Name = "Anything", IsRequired = true, EmitDefaultValue = false)]
public DateTime Anything{ get; set; }
[DataMember(Order = 3, Name = "Booleanthing", IsRequired = true, EmitDefaultValue = false)]
public bool Booleanthing{ get; set; }
}
As a side note; even though you don't control the generation of the XML you can certainly modify it's structure using Linq to Xml before deserializing it into a concrete class. Here's just a sample of a transformation I'm working on at the moment. By transforming the XML you prevent the XML structure from dictating how your concrete classes look and fit together and are free to do OOP the way you like as it should be.
protected string OnTransformXml(string xml)
{
var doc = XDocument.Parse(xml);
var root = doc.Descendants("root").FirstOrDefault();
var allResults = doc.Descendants("Result").ToList();
foreach (var node in allResults)
{
// remove all Result elements from their current location
node.Remove();
// rename the Result element to RawResult
node.Name = "RawResult";
// create a container for the Result elements to live in
var rawMembersElement = new XElement("Result");
// add each Result element to the new container
rawMembersElement.Add(node);
// add the new container
root.Add(rawMembersElement);
}
return doc.ToString();
}
Transforms this:
<root>
<Result>
<a>1</a>
<b></b>
</Result>
</root>
to this:
<root>
<Result>
<RawResult>
<a>1</a>
<b></b>
</RawResult>
</Result>
</root>
Upvotes: 2