2016-08-02 68 views
3

我想创建一个紧密符合C#对象图及其JSON表示的XML文档,但是在XML中的列表表示方面遇到困难。鉴于这种图表如何使用JSON与数组项的父节点生成XML

public class X 
{ 
    public List<A> Aa { get; set; } 
} 

public class A 
{ 
    public int B; 
    public bool C; 
} 

我把JSON从上面,并试图将其转换几种方法:

var json = @"{""Aa"":[{""B"":186,""C"":true},{""B"":9,""C"":false},{""B"":182,""C"":true}]}"; 
var xml = JsonConvert.DeserializeXNode(json, typeof(T).Name, false); 
var xml2 = JsonToXml(json); 

这产生用于xml(无Aa“容器节点”)执行以下操作:

<X> 
    <Aa><B>186</B><C>true</C></Aa> 
    <Aa><B>9</B><C>false</C></Aa> 
    <Aa><B>182</B><C>true</C></Aa> 
</X> 

并为xml2(具有 “容器” 节点,但一些额外的噪声):

<root type="object"> 
    <Aa type="array"> 
    <item type="object"> 
     <B type="number">186</B> 
     <C type="boolean">true</C> 
    </item> 
    <item type="object"> 
     <B type="number">9</B> 
     <C type="boolean">false</C> 
    </item> 
    <item type="object"> 
     <B type="number">182</B> 
     <C type="boolean">true</C> 
    </item> 
    </Aa> 
</root> 

用于生产值xml2的方法来自于different approach using the .NET Framework

XDocument JsonToXml(string jsonString) 
    { 
     using (var stream = new MemoryStream(Encoding.ASCII.GetBytes(jsonString))) 
     { 
      var quotas = new XmlDictionaryReaderQuotas(); 
      return XDocument.Load(JsonReaderWriterFactory.CreateJsonReader(stream, quotas)); 
     } 
    } 

我想产生是

<X> 
    <Aa> 
    <A><B>186</B><C>true</C></A> 
    <A><B>9</B><C>false</C></A> 
    <A><B>182</B><C>true</C></A> 
    </Aa> 
</X> 

我试图改变的DeserializeXDocumentwriteArrayAttribute参数设置为true ,但那也行不通。 converting between JSON and XML的文档不起作用。

如何生成包含父节点Aa节点中的项目的精简版本?这是否需要一些自定义解串器?

原始JSON经由

var json = JsonConvert.SerializeObject(new X { etc }, Formatting.None, settings); 
+1

那么JSON看起来不是这样吗? '{“Aa”:{“B”:[30,2,31]}}'或类似的东西?您输入的XML输出对我来说看起来是正确的,因为您请求了一系列'Aa'项目,而不是'B'项目。你可以尝试构建出序列化到你想要的XML的POCO,然后序列化为JSON,看看它做了什么,这样你就可以比较两者。 – TyCobb

+0

我更新了一个稍微不太人为的例子。 – Kit

+0

'{“Aa”:{“A”:[{“B”:186,“C”:true},{“B”:9,“C”:false},{“B”:182, “:true}]}}'应该产生你想要的所需的XML。数组的键将用作数组内每个对象/值的父元素。您的原始XML看起来很愚蠢可能是因为您错过了XmlSerialization属性,它告诉它它需要如何序列化。 ''[XmlElement(“A”)]'可能会删除'item'标记,或者可能已经完成了[XmlArray(“Aa”)] [XmlArrayItem(“A”)]'产生父'Aa '和孩子'A'。 – TyCobb

回答

2

的问题产生。

您的困难就出现了,因为有两种常用的方法将集合序列化为XML,而Json.NET仅支持其中一个JSON到XML的自动转换。

具体来说,当将c#集合序列化为XML(带有XmlSerializer)时,可以使用或不使用外部容器元素对集合进行序列化。前者看起来如下:

<X> 
    <Aa> 
    <A> 
     <B>186</B> 
     <C>true</C> 
    </A> 
    <A> 
     <B>9</B> 
     <C>false</C> 
    </A> 
    </Aa> 
</X> 

而后者看起来像:

<X> 
    <Aa> 
    <B>186</B> 
    <C>true</C> 
    </Aa> 
    <Aa> 
    <B>9</B> 
    <C>false</C> 
    </Aa> 
</X> 

当Json.NET转换JSON数组到XML元素,它采用所述第二格式为阵列,因为JSON只包含一个属性名称,而两级XML格式需要内部和外部元素名称。即在您的JSON中:

{"Aa":[{"B":186,"C":true},{"B":9,"C":false}]} 

仅显示名称"Aa""A"这个名字永远不会,所以DeserializeXNode()无法知道插入它。这使得第二种格式是规范转换的直接选择,而您需要第一种格式。

解决方案。

要从JSON数组生成两级XML集合,您需要在转换之前插入合成JSON对象,或者之后插入合成XML元素。

var jObject = JObject.Parse(json); 

jObject.SelectTokens("Aa").WrapWithObjects("A"); 

var finalXml = jObject.ToXElement(typeof(X).Name, false); 

使用扩展方法:对于前者,可以通过解析JSON字符串中间JToken,并修改它如下进行

public static class JsonExtensions 
{ 
    public static void WrapWithObjects(this IEnumerable<JToken> values, string name) 
    { 
     foreach (var value in values.ToList()) 
     { 
      var newParent = new JObject(); 
      if (value.Parent != null) 
       value.Replace(newParent); 
      newParent[name] = value; 
     } 
    } 

    public static XElement ToXElement(this JObject obj, string deserializeRootElementName = null, bool writeArrayAttribute = false) 
    { 
     if (obj == null) 
      return null; 
     using (var reader = obj.CreateReader()) 
      return JsonExtensions.DeserializeXElement(reader, deserializeRootElementName, writeArrayAttribute); 
    } 

    static XElement DeserializeXElement(JsonReader reader, string deserializeRootElementName, bool writeArrayAttribute) 
    { 
     var converter = new Newtonsoft.Json.Converters.XmlNodeConverter() { DeserializeRootElementName = deserializeRootElementName, WriteArrayAttribute = writeArrayAttribute }; 
     var jsonSerializer = JsonSerializer.CreateDefault(new JsonSerializerSettings { Converters = new JsonConverter[] { converter } }); 
     return jsonSerializer.Deserialize<XElement>(reader); 
    } 
} 

或者,你可以告诉XmlSerializer到(DE)通过标记它与[XmlElement]序列化Aa列表而不的容器元素:

public class X 
{ 
    [XmlElement] 
    public List<A> Aa { get; set; } 
} 

现在,由JsonConvert.DeserializeXNode生成的xml将直接反序列化。

+0

完美,谢谢!我采取了你的解决方案,并检查了'JObject.Type',以便我可以在整个图表中应用它。 – Kit