Reputation: 10055
I currently have the joy of writing an interface to a 3rd party whose wonderful webservice responds in different xml casing depending on the response type. Even given the same request the casing will be different depending on whether the result is successful, an error, the type of error etc. You get the point, its a nightmare.
As far as im aware there are no case insensitive deserializers available. The best ive gotten so far is to parse the xml into an XElement and trying some common casing such as Pasal Casing, Camel Casing, lower case etc.
Any better suggestions?
Upvotes: 2
Views: 1758
Reputation: 116990
One option would be to use an XSLT transform to convert all the node and attribute names to lower case before deserialization. See this answer for the necessary XSLT transform, and this question for instructions on using XSLT transforms in c#.
Another option would be to convert from XML to JSON using Json.NET as shown in Converting between JSON and XML, then deserialize with Json.NET, which is case insensitive. You need to be aware of one inconsistency between XML and JSON, namely that JSON has the concept of an array while XML does not and so uses repeating elements to represent arrays instead. Json.NET's XML-to-JSON converter detects repeating elements and converts them to arrays, but when an XML array has only one element this does not happen. In this situation it is necessary to follow the instructions from Convert XML to JSON and force array and add json:Array='true'
attributes into the XML.
Thus, you can introduce the following extension methods:
public static class JsonExtensions
{
const string JsonNamespace = @"http://james.newtonking.com/projects/json";
const string ArrayAttributeName = @"Array";
public static JToken ToJToken(this XElement xElement, bool omitRootObject, string deserializeRootElementName)
{
return xElement.ToJToken(omitRootObject, deserializeRootElementName, Enumerable.Empty<Func<XElement, IEnumerable<XElement>>>());
}
public static JToken ToJToken(this XElement xElement, bool omitRootObject, string deserializeRootElementName, IEnumerable<Func<XElement, IEnumerable<XElement>>> arrayQueries)
{
foreach (var query in arrayQueries)
{
var name = XName.Get(ArrayAttributeName, JsonNamespace);
foreach (var element in query(xElement))
{
element.SetAttributeValue(name, true);
}
}
// Convert to Linq to XML JObject
var settings = new JsonSerializerSettings { Converters = { new XmlNodeConverter { OmitRootObject = omitRootObject, DeserializeRootElementName = deserializeRootElementName } } };
var root = JToken.FromObject(xElement, JsonSerializer.CreateDefault(settings));
return root;
}
}
Then, given the following XML:
<root>
<perSon ID='1'>
<name>Alan</name>
<url>http://www.google.com</url>
</perSon>
</root>
And the following types:
public class Person
{
public string Name { get; set; }
public string URL { get; set; }
[XmlAttribute(AttributeName = "id")]
[JsonProperty("@id")]
public string Id { get; set; }
}
public class Root
{
[XmlElement]
public List<Person> Person { get; set; }
}
You can deserialize the XML as follows:
var xElement = XElement.Parse(xml);
var jToken = xElement.ToJToken(true, "",
new Func<XElement, IEnumerable<XElement>> [] { e => e.Elements().Where(i => string.Equals(i.Name.LocalName, "Person", StringComparison.OrdinalIgnoreCase)) });
var root = jToken.ToObject<Root>();
Sample fiddle.
Upvotes: 2