2016-08-02 69 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