无论如何,我可以阻止创建额外的"对象节点"在使用json.net?

时间:2016-06-06 16:24:40

标签: c# json xml json.net

我尝试使用Json.netJsonConvert.SerializeXNode)将xml转换为json。

如果您不使用某种模式(xsd),尝试在xml和json之间进行转换时会出现问题,因为您无法真正识别xml集合与xml集合之间的区别常规对象的单个元素。

示例:

<Drivers>
  <Driver>
    <Name>MyName</Name>
  </Driver>
</Drivers>

将转换为:

"Drivers":{ "Driver": { "Name": "MyName" } }

因为没有人告诉序列化程序,驱动程序是一个包含单个对象的集合,并且它认为它只是一个常规对象。

Json.net可以使用json:Array='true'标记解决此问题。

标记数组时,一切都很有效,但它会创建一个额外的中间对象(驱动程序):

"Drivers": [{"Driver":{"Name": "MyName"}}]

现在我明白了为什么要创建这个节点,但是我试图找到一种绕过创建的方法。我想得到这个结果:

"Drivers": [{"Name": "MyName"}]

有没有人知道如何做这样的事情?

3 个答案:

答案 0 :(得分:1)

Json.NET不会自动将使用outer container element序列化的集合(例如,附加[XmlArray]生成的集合)转换为单个JSON数组。相反,您需要通过使用LINQ-to-XML预处理XML或使用LINQ-to-JSON进行后处理来手动压缩不需要的嵌套级别。

由于您已经预处理XML以添加json:Array='true'属性,因此添加一些额外的预处理似乎是最直接的。首先,介绍以下扩展方法:

public static class XNodeExtensions
{
    /// <summary>
    /// Flatten a two-level collection with an outer container element to a one-level collection 
    /// in preparation for conversion to JSON using Json.NET
    /// </summary>
    /// <param name="parents">The outer container elements.</param>
    /// <param name="childName">The inner element name.  If null, flatten all children.</param>
    /// <param name="newChildName">The new element name.  If null, use the parent name.</param>
    public static void FlattenCollection(this IEnumerable<XElement> parents, XName childName = null, XName newChildName = null)
    {
        if (parents == null)
            throw new ArgumentNullException();

        XNamespace json = @"http://james.newtonking.com/projects/json";
        XName isArray = json + "Array";

        foreach (var parent in parents.ToList())
        {
            if (parent.Parent == null)
                continue; // Removed or root
            foreach (var child in (childName == null ? parent.Elements() : parent.Elements(childName)).ToList())
            {
                child.Remove();
                child.Name = newChildName ?? parent.Name;
                child.Add(new XAttribute(isArray, true));
                parent.Parent.Add(child);
            }
            if (!parent.HasElements)
                parent.Remove();
        }
    }
}

您现在可以:

var xnode = XDocument.Parse(xml);
xnode.Descendants("Drivers").FlattenCollection();
var json = JsonConvert.SerializeXNode(xnode, Formatting.Indented, true);

获取您想要的JSON。样本fiddle

请注意<Drivers>不能成为root element,因为XML文档必须只有一个根节点。

答案 1 :(得分:0)

运行以下示例并将标志omitRootObject设置为true将执行此操作。如果你把它变成假,你确实得到了上面的驱动程序节点 - 你正试图摆脱它。

    XmlDocument xmlDoc = new XmlDocument();
    xmlDoc.LoadXml(@"<Drivers>
                  <Driver>
                    <Name>MyName</Name>
                  </Driver>
                </Drivers>");


    var result = JsonConvert.SerializeXmlNode(xmlDoc.FirstChild, Newtonsoft.Json.Formatting.Indented, true);

    Console.WriteLine(result);

输出:

enter image description here

omitRootNode设置为false,您将获得正在尝试删除的根节点。

enter image description here

答案 2 :(得分:0)

我实施的扁平化有点不同。我已经接受了dbc的回答,因为它确实回答了这个问题,但这是我的实现,以防万一将来遇到这个并需要它:

<video width="320" height="240" controls>
  <source src="movie.mp4" type="video/mp4">
  <source src="movie.ogg" type="video/ogg">
  <source src="movie.webm" type="video/webm">
Your browser does not support the video tag.
</video>