C#Xml序列,收集和根元素(C# Xml serialization, collection an

2019-06-26 17:21发布

我的应用程序在流序列化对象。 这里是什么,我需要一个示例:

<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将和plurialize它自己产生XmlRootAttribute,但导致问题反序列化+项目名称时,仍保持着类名)

任何的想法 ?

真正困扰我的这个问题,是我想要的似乎是真的真的很简单...

Answer 1:

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

为了实现这一目标,我们需要告诉串行使用什么根节点,它是一种棘手的...

1)使用的序列化的对象上“XmlType将”

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

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

2)的代码,一个“智能根检测器 - 用于收集”的方法,这将返回一个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的(在例如静态字典)。

在我的测试,instanciating一个XmlSerializer的不XmlRootAttributes需要3毫秒。 如果指定了XmlRootAttribute,大约需要80毫秒(正好有一个自定义的根节点的名字!)



Answer 2:

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的问题是,当你给它的仿制药,它与泛型响应。 清单implemets阵的地方在那里和序列化的结果将几乎永远是ArrayOf<X> 要解决这个问题,你可以命名的财产,或者该类根。 你需要什么样的关闭可能是从你的例子第二个版本。 林假设你试图对象列表链接的直接序列化。 这是行不通的,因为你没有指定根节点。 现在,类似的方法可以发现在这里 。 在这其中,他们宣称串行时指定XmlRootAttribute。 你是这样的:

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


Answer 3:

干得好...

 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; }

}


文章来源: C# Xml serialization, collection and root element