Json/Xml 的強型別資料轉換
阿新 • • 發佈:2020-08-26
最近都在搞這東西, 雖然市面上很多 Json2CSharp / Xml2CSharp 的東西, 不過幾乎都不對, 在生成 CSharp 型別的時候歸併得不好, 他們的邏輯大致就是根據節點名稱來生成型別, 然後如果名稱相同的話, 就歸併到一起, 可是很多時候同名節點下有同名的物件, 可以它們型別不同的時候, 就完蛋了, 直接看看下面一個例子, 從 XML 結構生成 C# 程式碼的 :
XML :
<?xml version="1.0" encoding="UTF-8"?> <info> <entry <!-- 測試List --> path="E:\ModulesProjects_CheckOut\ArtistFiles\Assets" revision="553" kind="dir"> <entry name="HH"> <!-- 測試重複型別 --> <user>ME</user> <url name="SB"></url> <!-- 測試重複變數 --> </entry> <url>https://desktop-82s9bq9/svn/UnityProjects/ArtistFiles/Assets</url> </entry> <entry <!-- 測試List --> revision="6" kind="dir" path="E:\ModulesProjects_CheckOut\ArtistFiles\Assets\DataConverterModules\Editor"> <entry name="HH"> <!-- 測試重複型別 --> <user>ME</user> <url name="SB"></url> <!-- 測試重複變數 --> </entry> <url>https://desktop-82s9bq9/svn/DataConverterModules/Assets/DataConverterModules/Editor</url> </entry> </info>
可以看到這裡故意使用同名節點 <entry>/<url> 並且 <url> 節點都在 <entry> 節點下面, 並且型別不同 :
<entry path="E:\ModulesProjects_CheckOut\ArtistFiles\Assets" revision="553" kind="dir"> <entry name="HH"> <user>ME</user> <url name="SB"></url> <!-- 帶Attribute --> </entry> <url>https://desktop-82s9bq9/svn/UnityProjects/ArtistFiles/Assets</url> <!-- 普通Element --> </entry>
然後找個 Xml2CSharp 線上轉換的轉換一下, 得到下面的程式碼 (https://xmltocsharp.azurewebsites.net/) :
using System; using System.Xml.Serialization; using System.Collections.Generic; namespace Xml2CSharp { [XmlRoot(ElementName="url")] public class Url { [XmlAttribute(AttributeName="name")] public string Name { get; set; } } [XmlRoot(ElementName="entry")] public class Entry { [XmlElement(ElementName="user")] public string User { get; set; } [XmlElement(ElementName="url")] public Url Url { get; set; } // 節點下的 string 型別 url 被 URL 型別覆蓋了 [XmlAttribute(AttributeName="name")] public string Name { get; set; } [XmlElement(ElementName="entry")] public Entry Entry { get; set; } [XmlAttribute(AttributeName="revision")] public string Revision { get; set; } [XmlAttribute(AttributeName="kind")] public string Kind { get; set; } [XmlAttribute(AttributeName="path")] public string Path { get; set; } } [XmlRoot(ElementName="info")] public class Info { [XmlElement(ElementName="entry")] public List<Entry> Entry { get; set; } } }
這就不對了, 即使反序列化可以執行, 可是我少了一個網址的 url 節點啊, 可以看出它的邏輯就是同名型別歸併, 看到 Entry 型別裡面還包含了 Entry, 就跟 XML 節點一樣, 前面也說了, 這樣歸併下來的話, 同樣是 Url 節點, 它就衝突了, 會變成 :
public string Url {get;set;} public Url Url {get;set;}
這樣肯定不行, 上面就是後寫入的 Url 型別變數覆蓋了 string 型別變數, 並且還有隱患的是節點型別 [XmlElement] 和 [XmlAttribute] 也是可能衝突的, 所以上面的簡單轉換並沒有實用價值.
先來看結論, 目前我製作的轉換工具得到的結果:
XMLToCSharp :
using System; using System.Collections; using System.Collections.Generic; using System.Text; using System.Xml; using System.Xml.Serialization; using System.Xml.Schema; using System.IO; namespace DataConverterModules { [XmlRoot(ElementName="info")] public class info { [XmlRoot(ElementName="entry")] public class Merge_1_entry { [XmlAttribute(AttributeName="path")] public string path; [XmlAttribute(AttributeName="revision")] public string revision; [XmlAttribute(AttributeName="kind")] public string kind; [XmlElement(ElementName="entry")] public Merge_2_entry entry; // 歸併唯一性的結果, 不同的型別被分離了 [XmlElement(ElementName="url")] public string url; // 正確保留了變數 } [XmlRoot(ElementName="entry")] public class Merge_2_entry { [XmlAttribute(AttributeName="name")] public string name; [XmlElement(ElementName="user")] public string user; [XmlElement(ElementName="url")] public Merge_3_url url; } [XmlRoot(ElementName="url")] public class Merge_3_url { [XmlAttribute(AttributeName="name")] public string name; } [XmlElement(ElementName="entry")] public List<Merge_1_entry> entry; // 型別名稱跟節點名稱不同, 這是歸併唯一性的結果 } }
對於節點衝突通過另一種歸併型別的方式實現, 雖然這裡看不出來不過保留了正確的變數