簡體   English   中英

C#Xml序列化,集合和根元素

[英]C# Xml serialization, collection and root element

我的應用程序序列化流中的對象。 以下是我需要的樣本:

<links>
  <link href="/users" rel="users" />
  <link href="/features" rel="features" />
</links>

在這種情況下,對象是“鏈接”對象的集合。

-----------第一版

起初我使用了DataContractSerializer ,但是你不能將成員序列化為屬性(

這是對象:

[DataContract(Name="link")]
public class LinkV1
{
    [DataMember(Name="href")]
    public string Url { get; set; }

    [DataMember(Name="rel")]
    public string Relationship { get; set; }
}

這是結果:

<ArrayOflink xmlns:i="...." xmlns="...">
  <link>
    <href>/users</href>
    <rel>users</rel>
  </link>
  <link>
    <href>/features</href>
    <rel>features</rel>
  </link>
</ArrayOflink>

-----------第二版

好吧,不要安靜我想要的東西,所以我嘗試了經典的XmlSerializer ,但是......哦,不,如果根元素是一個集合,你不能指定根元素和集合元素的名稱......

這是代碼:

[XmlRoot("link")]
public class LinkV2
{
    [XmlAttribute("href")]
    public string Url { get; set; }

    [XmlAttribute("rel")]
    public string Relationship { get; set; }
}

結果如下:

<ArrayOfLinkV2>
  <LinkV2 href="/users" rel="users" />
  <LinkV2 href="/features" rel="features" />
  <LinkV2 href="/features/user/{keyUser}" rel="featuresByUser" />
</ArrayOfLinkV2>

-----------第三版

使用XmlSerializer +一個根元素:

[XmlRoot("trick")]
public class TotallyUselessClass
{
    [XmlArray("links"), XmlArrayItem("link")]
    public List<LinkV2> Links { get; set; }
}

結果如下:

 <trick>
  <links>
    <link href="/users" rel="users" />
    <link href="/features" rel="features" />
    <link href="/features/user/{keyUser}" rel="featuresByUser" />
  </links>
</trick>

很好,但我不想要那個根節點!! 我希望我的集合成為根節點。

以下是約束:

  • 序列化代碼是通用的,它適用於任何可序列化的
  • 逆操作(反序列化)也必須工作
  • 我不想正則表達式結果(我直接在輸出流中序列化)

我現在有什么解決方案:

  1. 編碼我自己的XmlSerializer
  2. 當它與集合一起工作時欺騙XmlSerializer(我試過,讓它找到一個XmlRootElement並將其多重化以生成它自己的XmlRootAttribute,但這會在反序列化時引起問題+項目名稱仍保留類名)

任何的想法 ?

在這個問題上真正困擾我的是,我想要的似乎真的非常簡單......

好的,這是我的最終解決方案(希望它可以幫助某人),它可以序列化一個普通數組,List <>,HashSet <>,...

為了實現這一點,我們需要告訴序列化器使用什么根節點,這有點棘手......

1)在可序列化對象上使用'XmlType'

[XmlType("link")]
public class LinkFinalVersion
{
    [XmlAttribute("href")]
    public string Url { get; set; }

    [XmlAttribute("rel")]
    public string Relationship { get; set; }
}

2)編寫一個'smart-root-detector-for-collection'方法,它將返回一個XmlRootAttribute

private XmlRootAttribute XmlRootForCollection(Type type)
{
    XmlRootAttribute result = null;

    Type typeInner = null;
    if(type.IsGenericType)
    {
        var typeGeneric = type.GetGenericArguments()[0];
        var typeCollection = typeof (ICollection<>).MakeGenericType(typeGeneric);
        if(typeCollection.IsAssignableFrom(type))
            typeInner = typeGeneric;
    }
    else if(typeof (ICollection).IsAssignableFrom(type)
        && type.HasElementType)
    {
        typeInner = type.GetElementType();
    }

    // yeepeeh ! if we are working with a collection
    if(typeInner != null)
    {
        var attributes = typeInner.GetCustomAttributes(typeof (XmlTypeAttribute), true);
        if((attributes != null)
            && (attributes.Length > 0))
        {
            var typeName = (attributes[0] as XmlTypeAttribute).TypeName + 's';
            result = new XmlRootAttribute(typeName);
        }
    }
    return result;
}

3)將XmlRootAttribute推送到序列化程序

// hack : get the XmlRootAttribute if the item is a collection
var root = XmlRootForCollection(type);
// create the serializer
var serializer = new XmlSerializer(type, root);

我告訴過你這很棘手;)


為了改善這一點,您可以:

A)創建XmlTypeInCollectionAttribute以指定自定義根名稱(如果基本復數不符合您的需要)

[XmlType("link")]
[XmlTypeInCollection("links")]
public class LinkFinalVersion
{
}

B)如果可能,緩存您的XmlSerializer(例如,在靜態字典中)。

在我的測試中,在沒有XmlRootAttributes的情況下實現XmlSerializer需要3ms。 如果指定XmlRootAttribute,則需要大約80ms(只是為了擁有自定義根節點名稱!)

XmlSerializer應該能夠滿足您的需求,但它高度依賴於初始結構和設置。 我在自己的代碼中使用它來生成非常相似的東西。

public class Links<Link> : BaseArrayClass<Link> //use whatever base collection extension you actually need here
{
    //...stuff...//
}

public class Link
{
    [XmlAttribute("href")]
    public string Url { get; set; }

    [XmlAttribute("rel")]
    public string Relationship { get; set; }
}

現在,序列化Links類應該生成您正在尋找的內容。

XmlSerializer的問題是當你給它泛型時,它會用泛型作為響應。 List的實現Array在那里的某處,序列化結果幾乎總是ArrayOf<X> 要解決這個問題,您可以命名屬性或類根。 您需要的內容可能是您示例中的第二版。 我假設您嘗試直接序列化對象列表鏈接。 這不起作用,因為您沒有指定根節點。 現在,可以在這里找到類似的方法。 在這一個中,他們在聲明序列化器時指定XmlRootAttribute。 你的看起來像這樣:

XmlSerializer xs = new XmlSerializer(typeof(List<Link>), new XmlRootAttribute("Links"));

干得好...

 class Program
{
    static void Main(string[] args)
    {

        Links ls = new Links();
        ls.Link.Add(new Link() { Name = "Mike", Url = "www.xml.com" });
        ls.Link.Add(new Link() { Name = "Jim", Url = "www.xml.com" });
        ls.Link.Add(new Link() { Name = "Peter", Url = "www.xml.com" });

        XmlSerializer xmlSerializer = new XmlSerializer(typeof(Links));

        StringWriter stringWriter = new StringWriter();

        xmlSerializer.Serialize(stringWriter, ls);

        string serializedXML = stringWriter.ToString();

        Console.WriteLine(serializedXML);

        Console.ReadLine();
    }
}

[XmlRoot("Links")]
public class Links
{
    public Links()
    {
        Link = new List<Link>();
    }

    [XmlElement]
    public List<Link> Link { get; set; }
}

[XmlType("Link")]
public class Link
{
    [XmlAttribute("Name")]
    public string Name { get; set; }


    [XmlAttribute("Href")]
    public string Url { get; set; }

}

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM