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

Mos*_*ose 11 c# xml collections serialization root-node

我的应用程序序列化流中的对象.以下是我需要的样本:

<links>
  <link href="/users" rel="users" />
  <link href="/features" rel="features" />
</links>
Run Code Online (Sandbox Code Playgroud)

在这种情况下,对象是"链接"对象的集合.

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

起初我使用了DataContractSerializer,但是你不能将成员序列化为属性()

这是对象:

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

    [DataMember(Name="rel")]
    public string Relationship { get; set; }
}
Run Code Online (Sandbox Code Playgroud)

这是结果:

<ArrayOflink xmlns:i="...." xmlns="...">
  <link>
    <href>/users</href>
    <rel>users</rel>
  </link>
  <link>
    <href>/features</href>
    <rel>features</rel>
  </link>
</ArrayOflink>
Run Code Online (Sandbox Code Playgroud)

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

好吧,不要安静我想要的东西,所以我尝试了经典的XmlSerializer,但是......哦,不,如果根元素是一个集合,你不能指定根元素和集合元素的名称......

这是代码:

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

    [XmlAttribute("rel")]
    public string Relationship { get; set; }
}
Run Code Online (Sandbox Code Playgroud)

结果如下:

<ArrayOfLinkV2>
  <LinkV2 href="/users" rel="users" />
  <LinkV2 href="/features" rel="features" />
  <LinkV2 href="/features/user/{keyUser}" rel="featuresByUser" />
</ArrayOfLinkV2>
Run Code Online (Sandbox Code Playgroud)

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

使用XmlSerializer +一个根元素:

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

结果如下:

 <trick>
  <links>
    <link href="/users" rel="users" />
    <link href="/features" rel="features" />
    <link href="/features/user/{keyUser}" rel="featuresByUser" />
  </links>
</trick>
Run Code Online (Sandbox Code Playgroud)

很好,但我不想要那个根节点!! 我希望我的集合成为根节点.

以下是约束:

  • 序列化代码是通用的,它适用于任何可序列化的
  • 逆操作(反序列化)也必须工作
  • 我不想正则表达式结果(我直接在输出流中序列化)

我现在有什么解决方案:

  1. 编码我自己的XmlSerializer
  2. 当它与集合一起工作时欺骗XmlSerializer(我试过,让它找到一个XmlRootElement并将其多重化以生成它自己的XmlRootAttribute,但这会在反序列化时引起问题+项目名称仍保留类名)

任何的想法 ?

在这个问题上真正困扰我的是,我想要的似乎真的非常简单......

Mos*_*ose 8

好的,这是我的最终解决方案(希望它可以帮助某人),可以序列化一个普通数组,List <>,HashSet <>,...

为了实现这一点,我们需要告诉序列化器使用什么根节点,这有点棘手......

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

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

    [XmlAttribute("rel")]
    public string Relationship { get; set; }
}
Run Code Online (Sandbox Code Playgroud)

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;
}
Run Code Online (Sandbox Code Playgroud)

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);
Run Code Online (Sandbox Code Playgroud)

我告诉过你这很棘手;)


为了改善这一点,您可以:

A)创建XmlTypeInCollectionAttribute以指定自定义根名称(如果基本复数不符合您的需要)

[XmlType("link")]
[XmlTypeInCollection("links")]
public class LinkFinalVersion
{
}
Run Code Online (Sandbox Code Playgroud)

B)如果可能,缓存您的XmlSerializer(例如,在静态字典中).

在我的测试中,在没有XmlRootAttributes的情况下实现XmlSerializer需要3ms.如果指定XmlRootAttribute,则需要大约80ms(只是为了拥有自定义根节点名称!)