【问题标题】:JSON.Net Xml Serialization misunderstands arraysJSON.Net Xml 序列化误解了数组
【发布时间】:2013-01-23 20:21:26
【问题描述】:

我有一些自动生成的 xml,其中 xml 的某些部分可能有多行,而有些可能没有。结果是,如果有一行,则返回单个 json 节点,如果我有多行,则返回带有 json 节点的数组。

xmls 可能看起来像这样

<List>
    <Content>
        <Row Index="0">
            <Title>Testing</Title>
            <PercentComplete>0</PercentComplete>
            <DueDate/>
            <StartDate/>
        </Row>
    </Content>
</List>

或多行

<List>
    <Content>
        <Row Index="0">
            <Title>Update Documentation</Title>
            <PercentComplete>0.5</PercentComplete>
            <DueDate>2013-01-31 00:00:00</DueDate>
            <StartDate>2013-01-01 00:00:00</StartDate>
        </Row>
        <Row Index="1">
            <Title>Write jQuery example</Title>
            <PercentComplete>0.05</PercentComplete>
            <DueDate>2013-06-30 00:00:00</DueDate>
            <StartDate>2013-01-02 00:00:00</StartDate>
        </Row>
    </Content>
</List>

当使用将这些序列化为 JSON 时

JsonConvert.SerializeXmlNode(xmldoc, Formatting.Indented);

第一个xml变成这个

{
    "List": {
        "Content": {
            "Row": {
                "@Index": "0",
                "Title": "Testing",
                "PercentComplete": "0",
                "DueDate": null,
                "StartDate": null
            }
        }
    }
}

第二个

{
    "List": {
        "Content": {
            "Row": [{
                "@Index": "0",
                "Title": "Update Documentation",
                "PercentComplete": "0.5",
                "DueDate": "2013-01-31 00:00:00",
                "StartDate": "2013-01-01 00:00:00"
            }, {
                "@Index": "1",
                "Title": "Write jQuery example",
                "PercentComplete": "0.05",
                "DueDate": "2013-06-30 00:00:00",
                "StartDate": "2013-01-02 00:00:00"
            }]
        }
    }
}

可以清楚地看到第二个的 Row 是一个数组,但不是第一个。这类问题是否有任何已知的解决方法,或者我是否需要在接收 JSON 的前端实施检查(这会有点问题,因为结构非常动态)。最好的方法是如果有任何方法可以强制 json.net 始终返回数组。

【问题讨论】:

  • 我发现同样的问题,如果 (XDocument.Parse("5.0021.0045.00").Descendants("row").Count() > 1) { } if (XDocument.Parse("1.005.0045.006.0010.0065.0011.00100.0098.00").Descendants("row").Count() > 1) { }

标签: c# json json.net


【解决方案1】:

来自 Json.NET 文档: http://james.newtonking.com/projects/json/help/?topic=html/ConvertingJSONandXML.htm

您可以通过将属性 json:Array='true' 添加到要转换为 JSON 的 XML 节点来强制将节点呈现为数组。此外,您需要在 XML 标头 xmlns:json='http://james.newtonking.com/projects/json' 处声明 json 前缀命名空间,否则您将收到一个 XML 错误,指出未声明 json 前缀。

下一个示例由文档提供:

xml = @"<person xmlns:json='http://james.newtonking.com/projects/json' id='1'>
        <name>Alan</name>
        <url>http://www.google.com</url>
        <role json:Array='true'>Admin</role>
      </person>";

生成的输出:

{
  "person": {
    "@id": "1",
    "name": "Alan",
    "url": "http://www.google.com",
    "role": [
      "Admin"
    ]
  }
}

【讨论】:

  • 同样的事情,但反过来呢?从 json 到 XML 并说想要将一个数组映射到单个 xml 节点,而不是每个数组元素一个?
  • json:Array='true' 在 .config 文件中使用时会出现错误“未定义命名空间前缀 'json'”。
【解决方案2】:

我确实像这样修复了这种行为

// Handle JsonConvert array bug
var rows = doc.SelectNodes("//Row");
if(rows.Count == 1)
{
    var contentNode = doc.SelectSingleNode("//List/Content");
    contentNode.AppendChild(doc.CreateNode("element", "Row", ""));

    // Convert to JSON and replace the empty element we created but keep the array declaration
    returnJson = JsonConvert.SerializeXmlNode(doc).Replace(",null]", "]");
}
else
{
    // Convert to JSON
    returnJson = JsonConvert.SerializeXmlNode(doc);
}

它有点脏,但它有效。我仍然对其他解决方案感兴趣!

【讨论】:

  • “不太脏”的解决方案,也对更好的解决方案感兴趣
【解决方案3】:

将我的 +1 给 Iván Pérez Gómez 并在此处提供一些代码来支持他的回答:

将所需的 json.net 命名空间添加到根节点:

private static void AddJsonNetRootAttribute(XmlDocument xmlD)
    {
        XmlAttribute jsonNS = xmlD.CreateAttribute("xmlns", "json", "http://www.w3.org/2000/xmlns/");
        jsonNS.Value = "http://james.newtonking.com/projects/json";

        xmlD.DocumentElement.SetAttributeNode(jsonNS);
    }

并将 json:Array 属性添加到 xpath 找到的元素:

private static void AddJsonArrayAttributesForXPath(string xpath, XmlDocument doc)
    {
        var elements = doc.SelectNodes(xpath);



        foreach (var element in elements)
        {
            var el = element as XmlElement;

            if (el != null)
            {

                var jsonArray = doc.CreateAttribute("json", "Array", "http://james.newtonking.com/projects/json");
                jsonArray.Value = "true";
                el.SetAttributeNode(jsonArray);
            }
        }
    }

这里是单个子节点作为 json 数组的示例:

【讨论】:

  • 这看起来很有趣,绝对是应该在源码中实现的东西
  • 同意,这将是一个比只处理字段或数组更安全的解决方案。在 Cocoa 中,原生 json 序列化程序总是为单个子节点创建一个数组,我觉得这是一种更一致的方法。
【解决方案4】:

我的解决方案:如果 JsonConvert 不起作用,请不要使用它。将 XML 解析为字典/集合,然后解析为 Json。至少这样您就不必对任何元素名称进行硬编码。

    private JsonResult AsJsonResult(XmlDocument result)
    {
        var kvp = new KeyValuePair<string, object>(result.DocumentElement.Name, Value(result.DocumentElement));

        return Json(kvp
             , JsonRequestBehavior.AllowGet);
    }

    /// <summary>
    /// Deserializing straight from Xml produces Ugly Json, convert to Dictionaries first to strip out unwanted nesting
    /// </summary>
    /// <param name="node"></param>
    /// <returns></returns>
    private object Value(XmlNode node)
    {
        dynamic value;

        //If we hit a complex element
        if (node.HasChildNodes && !(node.FirstChild is XmlText))
        {
            //If we hit a collection, it will have children which are also not just text!
            if (node.FirstChild.HasChildNodes && !(node.FirstChild.FirstChild is XmlText))
            {
                //want to return a list of Dictionarys for the children's nodes
                //Eat one level of the hierachy and return child nodes as an array
                value = new List<object>();
                foreach (XmlNode childNode in node.ChildNodes)
                {
                    value.Add(Value(childNode));
                }
            }
            else //regular complex element return childNodes as a dictionary
            {
                value = new Dictionary<string, object>();
                foreach (XmlNode childNode in node.ChildNodes)
                {
                    value.Add(childNode.Name, Value(childNode));
                }
            }
        }
        else //Simple element
        {
            value = node.FirstChild.InnerText;
        }

        return value;
    }

【讨论】:

    【解决方案5】:

    使用 XDocument 发现同样的问题

    if (XDocument.Parse("5.0021.0045.00").Descendants("row").Count() > 1) { }

                if (XDocument.Parse("<RUT3><row><FromKG>1.00</FromKG><ToKG>5.00</ToKG><Rate>45.00</Rate></row><row><FromKG>6.00</FromKG><ToKG>10.00</ToKG><Rate>65.00</Rate></row><row><FromKG>11.00</FromKG><ToKG>100.00</ToKG><Rate>98.00</Rate></row></RUT3>").Descendants("row").Count() > 1)
                {
    
                }
    

    【讨论】:

      【解决方案6】:

      更简单,在 JsonConvert.DeserializeXmlNode 中添加 bool 参数到可用的数组节点:

        var xml= JsonConvert.DeserializeXmlNode(dashstring, "root", true);
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2016-08-20
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2012-03-09
        • 1970-01-01
        相关资源
        最近更新 更多