1. 程式人生 > 實用技巧 >Json/Xml 的強型別資料轉換

Json/Xml 的強型別資料轉換

  最近都在搞這東西, 雖然市面上很多 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;        // 型別名稱跟節點名稱不同, 這是歸併唯一性的結果
    }
}

  對於節點衝突通過另一種歸併型別的方式實現, 雖然這裡看不出來不過保留了正確的變數