C#JsonConvert SerializeXmlNode为空,带有属性

时间:2017-12-28 14:34:25

标签: c# xml json.net

我使用JsonConvert SerializeXmlNode将xml转换为json。我面临的问题是我有一个标签,有时可以有价值,有时候是空的

<AustrittDatum>2018-01-31+01:00</AustrittDatum>
...
<AustrittDatum xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:nil="true"/>

结果 - 当我尝试使用字符串属性&#34; AustrittDatum&#34;将json反序列化为C#对象时,我遇到异常。 -   &#34; Newtonsoft.Json.JsonReaderException:&#39;读取字符串时出错。意外的令牌:StartObject。 Path&#39; AustrittDatum&#39;。&#39; &#34 ;,  因为

<AustrittDatum xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance xsi:nil="true"/> 

序列化为

"AustrittDatum": {
  "@xmlns:xsi": "http://www.w3.org/2001/XMLSchema-instance",
  "@xsi:nil": "true"
},

我如何强迫它成为像"AustrittDatum": ""这样的东西,或者可能是解决它的一些正确方法?

1 个答案:

答案 0 :(得分:1)

似乎在遇到带有xsi:nil="true"的XML元素时,Json.NET的XmlNodeConverter会创建一个带有您看到的属性的JSON对象,而不是null JToken。这与Newtonsoft文档页面Converting between JSON and XML

一致
  

会话规则

     
      
  • 元素保持不变。
  •   
  • 属性以@为前缀,应位于对象的开头。
  •   
  • 单个子文本节点是直接针对元素的值,否则可以通过#text。
  • 访问它们   
  • XML声明和处理说明以?。
  • 为前缀   
  • 字符数据,注释,空白和重要的空白节点分别通过#cdata-section,#comment,#whitespace和#significant-whitespace进行访问。
  •   
  • 同一级别具有相同名称的多个节点将组合在一起成为一个数组。
  •   
  • 空元素为空。
  •   
     

如果从JSON创建的XML与您想要的不匹配,那么您需要手动转换它...

尽管如此,我认为将xsi:nil="true"元素转换为null JSON值是合理的,因为xsi:nilpredefined w3c attribute。可能Newtonsoft没有这样做,因为这些元素可以携带额外的属性,如果要将元素转换为null,这些属性将会丢失。

如果您愿意,可以为XmlNodeConverter提交enhancement request,但与此同时,以下扩展方法将对JToken层次结构进行后处理并转换以前为{{1}的对象要使null JSON值为null的元素:

nil

然后使用它:

public static class JTokenExtensions
{
    const string XsiNamespace = @"http://www.w3.org/2001/XMLSchema-instance";
    readonly static string XmlNullValue = System.Xml.XmlConvert.ToString(true);

    public static JToken ReplaceXmlNilObjectsWithNull(this JToken root)
    {
        return root.ReplaceXmlNilObjects(t => JValue.CreateNull());
    }

    public static JToken ReplaceXmlNilObjects(this JToken root, Func<JToken, JToken> getReplacement)
    {
        var query = from obj in root.DescendantsAndSelf().OfType<JObject>()
                    where obj.Properties().Any(p => p.IsNilXmlTrueProperty())
                    select obj;
        foreach (var obj in query.ToList())
        {
            var replacement = getReplacement(obj);
            if (obj == root)
                root = replacement;
            if (obj.Parent != null)
                obj.Replace(replacement);
        }
        return root;
    }

    static IEnumerable<JToken> DescendantsAndSelf(this JToken node)
    {
        // Small wrapper adding this method to all JToken types.
        if (node == null)
            return Enumerable.Empty<JToken>();
        var container = node as JContainer;
        if (container != null)
            return container.DescendantsAndSelf();
        else
            return new[] { node };
    }

    static string GetXmlNamespace(this JProperty prop)
    {
        if (!prop.Name.StartsWith("@"))
            return null;
        var index = prop.Name.IndexOf(":");
        if (index < 0 || prop.Name.IndexOf(":", index+1) >= 0)
            return null;
        var ns = prop.Name.Substring(1, index - 1);
        if (string.IsNullOrEmpty(ns))
            return null;
        var nsPropertyName = "@xmlns:" + ns;
        foreach (var obj in prop.AncestorsAndSelf().OfType<JObject>())
        {
            var nsProperty = obj[nsPropertyName];
            if (nsProperty != null && nsProperty.Type == JTokenType.String)
                return (string)nsProperty;
        }
        return null;
    }

    static bool IsNilXmlTrueProperty(this JProperty prop)
    {
        if (prop == null)
            return false;
        if (!(prop.Value.Type == JTokenType.String && (string)prop.Value == "true"))
            return false;
        if (!(prop.Name.StartsWith("@") && prop.Name.EndsWith(":nil")))
            return false;
        var ns = prop.GetXmlNamespace();
        return ns == XsiNamespace;
    }
}

生成:

// Parse XML to XDocument
var xDoc = XDocument.Parse(xmlString);

// Convert the XDocument to an intermediate JToken hierarchy.
var converter = new Newtonsoft.Json.Converters.XmlNodeConverter { OmitRootObject = true };
var rootToken = JObject.FromObject(xDoc, JsonSerializer.CreateDefault(new JsonSerializerSettings { Converters = { converter } } ))
    // And replace xsi:nil objects will null JSON values
    .ReplaceXmlNilObjectsWithNull();

// Deserialize to the final RootObject.
var rootObject = rootToken.ToObject<RootObject>();

此处我最初解析为XDocument,但您也可以使用较早的XmlDocument

示例工作.Net fiddle