2010-09-01 50 views
2

我想弄清.net数组到XML的序列化。这里是一块,我已经拿出代码:Array到XML的.NET序列化。如何为数组类型设置别名?

public class Program 
    { 
     public class Person 
     { 
      public string Firstname { get; set; } 
      public string Lastname { get; set; } 
      public uint Age { get; set; } 
     } 

     static void Main() 
     { 
      Person[] p = 
      { 
       new Person{Age = 20, Firstname = "Michael", Lastname = "Jackson"}, 
       new Person{Age = 21, Firstname = "Bill", Lastname = "Gates"}, 
       new Person{Age = 22, Firstname = "Steve", Lastname = "Jobs"} 
      }; 

      SerializeObject<Person[]>(p); 
     } 

     static void SerializeObject<T>(T obj) where T : class 
     { 
      string fileName = Guid.NewGuid().ToString().Replace("-", "") + ".xml"; 
      using (FileStream fs = File.Create(fileName)) 
      { 
       XmlSerializerNamespaces ns = new XmlSerializerNamespaces(); 
       ns.Add("", ""); 
       XmlSerializer ser = new XmlSerializer(typeof(T)); 
       ser.Serialize(fs, obj, ns); 
      } 
     } 
    } 

下面是这个例子记到XML文件的XML内容:

<ArrayOfPerson> 
    <Person> 
    <Firstname>Michael</Firstname> 
    <Lastname>Jackson</Lastname> 
    <Age>20</Age> 
    </Person> 
    <Person> 
    <Firstname>Bill</Firstname> 
    <Lastname>Gates</Lastname> 
    <Age>21</Age> 
    </Person> 
    <Person> 
    <Firstname>Steve</Firstname> 
    <Lastname>Jobs</Lastname> 
    <Age>22</Age> 
    </Person> 
</ArrayOfPerson> 

但这并不是我真正想要的东西。我希望它看起来像这样:

<Persons> 
    <Person> 
    <Firstname>Michael</Firstname> 
    <Lastname>Jackson</Lastname> 
    <Age>20</Age> 
    </Person> 
    <Person> 
    <Firstname>Bill</Firstname> 
    <Lastname>Gates</Lastname> 
    <Age>21</Age> 
    </Person> 
    <Person> 
    <Firstname>Steve</Firstname> 
    <Lastname>Jobs</Lastname> 
    <Age>22</Age> 
    </Person> 
</Persons> 

我怎么能得到它的工作方式?提前致谢!

回答

5

你只需要进行一些小的改动你的代码,除了已经提供的建议。

首先SerializeObject通用方法需要被重新声明从而:

// important: declare the input parameter to be an **array** of T, not T. 
static void SerializeObject<T>(T[] obj) where T : class 
{ 
    string fileName = Guid.NewGuid().ToString().Replace("-", "") + ".xml"; 
    using (FileStream fs = File.Create(fileName)) 
    { 
     XmlSerializerNamespaces ns = new XmlSerializerNamespaces(); 
     ns.Add("", ""); 

     // override default root node name. based on your question, 
     // i'm just going to append an "s" to the base type 
     // (e.g., Person becomes Persons) 
     var rootName = typeof(T).Name + "s"; 
     XmlRootAttribute root = new XmlRootAttribute(rootName); 

     // add the attribute to the serializer constructor... 
     XmlSerializer ser = new XmlSerializer(obj.GetType(), root); 

     ser.Serialize(fs, obj, ns); 
    } 
} 

其次,在Main()方法,用SerializeObject<Person>(p)替换SerializeObject<Person[]>(p)。因此,你的Main()方法如下所示:

static void Main(string[] args) 
{ 
    Person[] p = 
    { 
     new Person{Age = 20, Firstname = "Michael", Lastname = "Jackson"}, 
     new Person{Age = 21, Firstname = "Bill", Lastname = "Gates"}, 
     new Person{Age = 22, Firstname = "Steve", Lastname = "Jobs"} 
    }; 

    SerializeObject<Person>(p); 
} 

生成的XML将看起来像这样:

<Persons> 
    <Person> 
    <Firstname>Michael</Firstname> 
    <Lastname>Jackson</Lastname> 
    <Age>20</Age> 
    </Person> 
    <Person> 
    <Firstname>Bill</Firstname> 
    <Lastname>Gates</Lastname> 
    <Age>21</Age> 
    </Person> 
    <Person> 
    <Firstname>Steve</Firstname> 
    <Lastname>Jobs</Lastname> 
    <Age>22</Age> 
    </Person> 
</Persons> 

要覆盖<Person>元素名称到别的东西,设置类的XmlType属性,像这样:

[XmlType("personEntry")] 
public class Person 
{ 
    public string Firstname { get; set; } 
    public string Lastname { get; set; } 
    public uint Age { get; set; } 
} 

生成的XML看起来是这样的:

<Persons> 
    <personEntry> 
    <Firstname>Michael</Firstname> 
    <Lastname>Jackson</Lastname> 
    <Age>20</Age> 
    </personEntry> 
    <personEntry> 
    <Firstname>Bill</Firstname> 
    <Lastname>Gates</Lastname> 
    <Age>21</Age> 
    </personEntry> 
    <personEntry> 
    <Firstname>Steve</Firstname> 
    <Lastname>Jobs</Lastname> 
    <Age>22</Age> 
    </personEntry> 
</Persons> 
+0

好东西!非常感谢你! – 2010-09-01 17:38:45

+0

考虑到这个代码,我可以以某种方式控制数组子节点的名称吗?例如渲染而不是 ... – 2010-09-02 05:40:31

+0

@the_V:是的,你可以。我更新了我的帖子。 – code4life 2010-09-02 13:52:47

4

你需要一个容器类,像这样:

/// <summary> 
    /// Represents an Person collection. 
    /// </summary> 
    [Serializable] 
    [XmlRoot("Persons", IsNullable = false)] 
    public sealed class Persons 
    { 
     /// <summary> 
     /// The person collection. 
     /// </summary> 
     private Collection<Person> persons; 

     /// <summary> 
     /// Initializes a new instance of the <see cref="Persons"/> class. 
     /// </summary> 
     /// <param name="persons">The person list.</param> 
     public Persons(Collection<Person> persons) 
     { 
      this.persons = persons; 
     } 

     /// <summary> 
     /// Initializes a new instance of the <see cref="Persons"/> class. 
     /// </summary> 
     /// <param name="persons">The person array.</param> 
     public Persons(Person[] persons) 
      : this(new Collection<Person>(persons)) 
     { 
     } 

     /// <summary> 
     /// Prevents a default instance of the <see cref="Persons"/> class from being created. 
     /// </summary> 
     private Persons() 
     { 
     } 

     /// <summary> 
     /// Copies the collection of Person objects to an array and returns 
     /// it. 
     /// </summary> 
     /// <returns>An array of Person objects based on the 
     /// collection.</returns> 
     public Person[] ToArray() 
     { 
      Person[] personArray = new Person[this.persons.Count]; 

      this.persons.CopyTo(personArray, 0); 
      return personArray; 
     } 

     /// <summary> 
     /// Gets or sets the persons. 
     /// </summary> 
     /// <value>The persons.</value> 
     [XmlElement("Person")] 
     public Collection<Person> ThePersons 
     { 
      get 
      { 
       return this.persons; 
      } 

      set 
      { 
       this.persons = value; 
      } 
     } 

     /// <summary> 
     /// Gets the length of the persons. 
     /// </summary> 
     /// <value>The length of the persons.</value> 
     [XmlIgnore] 
     public int Length 
     { 
      get 
      { 
       return this.persons.Count; 
      } 
     } 

     /// <summary> 
     /// Returns an enumerator that iterates through the collection. 
     /// </summary> 
     /// <returns>A <see cref="IEnumerator&lt;Person&gt;"/> that can be used to 
     /// iterate through the collection.</returns> 
     public IEnumerator<Person> GetEnumerator() 
     { 
      return (IEnumerator<Person>)this.persons.GetEnumerator(); 
     } 
    } 

你完成数组初始化它并返回按您的Main()方法:

static void Main() 
    { 
     Person[] p = 
     { 
      new Person{Age = 20, Firstname = "Michael", Lastname = "Jackson"}, 
      new Person{Age = 21, Firstname = "Bill", Lastname = "Gates"}, 
      new Person{Age = 22, Firstname = "Steve", Lastname = "Jobs"} 
     }; 

     SerializeObject<Persons>(new Persons(p)); 

     Person[] p2 = DeserializeObject<Persons>("filename.xml").ToArray(); 
    } 

解串器方法很简单,那么:

static T DeserializeObject<T>(string fileName) where T : class 
    { 
     using (FileStream fs = File.OpenRead(fileName)) 
     { 
      XmlSerializer ser = new XmlSerializer(typeof(T)); 
      return (T)ser.Deserialize(fs); 
     } 
    } 

选项2(建立在Nixanswer):

static void SerializeObject<T>(T obj, Type t) where T : class 
    { 
     string fileName = Guid.NewGuid().ToString().Replace("-", "") + ".xml"; 
     using (FileStream fs = File.Create(fileName)) 
     { 
      XmlSerializerNamespaces ns = new XmlSerializerNamespaces(); 
      ns.Add("", ""); 
      XmlRootAttribute root = new XmlRootAttribute(t.Name + "s"); 
      XmlSerializer ser = new XmlSerializer(typeof(T), root); 
      ser.Serialize(fs, obj, ns); 
     } 
    } 

可以被称为例如:

 SerializeObject<Person[]>(p, typeof(Person)); 
+0

+1,很好的回答(除了你不需要XML序列化的[Serializable]属性) – 2010-09-01 14:19:02

+0

@Thomas:真的,谢谢。我从一个项目中复制/粘贴了XML序列化和Remoting。 – 2010-09-01 14:41:36

+0

非常感谢,效果很棒! – 2010-09-01 16:14:39

1

您可以通过添加一个root属性到你的串行做到这一点。见下文。

static void SerializeObject<T>(T obj) where T : class 
    { 
     string fileName = Guid.NewGuid().ToString().Replace("-", "") + ".xml"; 
     using (FileStream fs = File.Create(fileName)) 
     { 
      XmlSerializerNamespaces ns = new XmlSerializerNamespaces(); 
      ns.Add("", ""); 

      XmlRootAttribute root = new XmlRootAttribute(typeof(T).Name + "s"); 

      XmlSerializer 
       ser = new XmlSerializer(typeof(Person[]), root); 
       ser.Serialize(fs, obj, ns); 
     } 
    } 

另外,你可以通过一个func来做名称选择。 您的代码将是

SerializeObject<Person[]>(p, per=>p.GetType().Name); 



static void SerializeObject<T>(T obj, Func<T,string> nameSelector) where T : class 
{ 
    string fileName = Guid.NewGuid().ToString().Replace("-", "") + ".xml"; 
    using (FileStream fs = File.Create(fileName)) 
    { 
     XmlSerializerNamespaces ns = new XmlSerializerNamespaces(); 
     ns.Add("", ""); 

     XmlRootAttribute root = new XmlRootAttribute(nameSelector(obj)); 

     XmlSerializer 
      ser = new XmlSerializer(typeof(Person[]), root); 
     ser.Serialize(fs, obj, ns); 
    } 
} 
+0

现在,您的解决方案仅适用于“人员”情况,而不适用于传入他的“SerializeObject”方法的任何其他通用“”。 – 2010-09-01 13:54:55

+0

你可以传入“ArrayName”参数。或者你可以选择这个类型并添加一个S,如果你真的想。 – Nix 2010-09-01 13:58:11

1

此外,通过属性控制序列化这个环节一直是有用的给我。

http://msdn.microsoft.com/en-us/library/2baksw0z(v=VS.100).aspx

+0

它不会帮助在这种情况下,因为你不能在阵列类型上添加一个属性 – 2010-09-01 14:17:23

+0

这是真的,但看到它是一个XML序列化的问题我以为我会分享一个链接,帮助我它在过去,并且如果_V做了Jesse C Slicer的建议,并且将这个人分成一个具有属性的单独类,那么它肯定会有帮助。 – asawyer 2010-09-01 14:35:02