2

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?

CathalMF
  • 9,705
  • 6
  • 70
  • 106
  • Possible duplicate of [Case insensitive Deserialization](https://stackoverflow.com/questions/3975354/case-insensitive-deserialization) – 0liveradam8 Aug 19 '17 at 21:58
  • See also [Case insensitive XML parser in c#](https://stackoverflow.com/q/9334771). – dbc Aug 20 '17 at 06:53
  • You could convert from XML to JSON using Json.NET as shown in [Converting between JSON and XML](https://www.newtonsoft.com/json/help/html/ConvertingJSONandXML.htm), then deserialize with Json.NET, which is case insensitive. – dbc Aug 20 '17 at 06:59
  • And another option would be to create an `XmlReader` [decorator](https://en.wikipedia.org/wiki/Decorator_pattern) as shown [here](http://referencesource.microsoft.com/#System.Xml/System/Xml/Core/XmlWrappingReader.cs) and [here](https://msdn.microsoft.com/en-us/library/t2abc1zd(v=vs.71).aspx) (under *Chaining XmlReaders*) then subclass the decorator and downcase all the element and property names. – dbc Aug 20 '17 at 07:03
  • @dbc using Json.NET worked perfectly. That really saved me a huge amount of hassle. Thanks. – CathalMF Aug 22 '17 at 14:28
  • @dbc Put that as an answer with example and ill mark it as correct. – CathalMF Aug 22 '17 at 14:29
  • @CathalMF - OK, done. – dbc Aug 22 '17 at 19:01

1 Answers1

2

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.

dbc
  • 104,963
  • 20
  • 228
  • 340