当元素的外壳不一致时反序列化/读取xml。

时间:2017-08-19 21:51:19

标签: c# xml parsing deserialization

我目前很高兴为第三方编写接口,其精彩的web服务根据响应类型以不同的xml大小写响应。即使给出相同的请求,套管也会有所不同,具体取决于结果是否成功,错误,错误类型等等。你得到了这一点,这是一场噩梦。

据我所知,没有可用的不区分大小写的反序列化器。 到目前为止,最好的ive是将xml解析为XElement并尝试一些常见的外壳,如Pasal Casing,Camel Casing,小写等。

有更好的建议吗?

1 个答案:

答案 0 :(得分:1)

一种选择是在反序列化之前使用XSLT转换将所有节点和属性名称转换为小写。有关必要的XSLT转换,请参阅this answer;有关在c#中使用XSLT转换的说明,请参阅this question

另一种选择是使用Json.NET从XML转换为JSON,如Converting between JSON and XML所示,然后使用Json.NET反序列化,即case insensitive。您需要了解XML和JSON之间的一个不一致,即JSON具有数组的概念,而XML则不然,因此使用重复元素来表示数组。 Json.NET的XML-to-JSON converter检测重复元素并将它们转换为数组,但是当XML数组只有一个元素时,这不会发生。在这种情况下,有必要按照Convert XML to JSON and force array中的说明操作,并将json:Array='true'属性添加到XML中。

因此,您可以介绍以下扩展方法:

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

然后,给出以下XML:

<root>
  <perSon ID='1'>
    <name>Alan</name>
    <url>http://www.google.com</url>
  </perSon>
</root>

以下类型:

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

您可以按如下方式反序列化XML:

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>();

示例fiddle