【问题标题】:XmlSerializer Convert C# object to xml stringXmlSerializer 将 C# 对象转换为 xml 字符串
【发布时间】:2013-07-19 06:20:55
【问题描述】:

我创建了一个 C# 类:

public class books {
    public int bookNum { get; set; }
    public class book {
        public string name { get; set; }
        public class record {
            public string borrowDate { get; set; }
            public string returnDate { get; set; }
        }
        public record[] records { get; set; }
    }
    public book[] books { get; set; }
}

但是当我使用 XmlSerializer 转换为 XML 字符串时。结果和下面的xml不一样。

我的 C# 类有什么问题?我想使用 XmlSerializer 来输出结果而不是使用 XmlDocument.

有什么想法吗?提前致谢!

<books>
    <bookNum>2</bookNum>
    <book>
        <name>Book 1</name>
        <record>
            <borrowDate>2013-7-1</borrowDate>
            <returnDate>2013-7-12</returnDate>
        </record>
        <record>            
            <borrowDate>2013-8-1</borrowDate>
            <returnDate>2013-8-5</returnDate>
        </record>
    </book>
    <book>
        <name>Book 2</name>
        <record>
            <borrowDate>2013-6-1</borrowDate>
            <returnDate>2013-6-12</returnDate>
        </record>
        <record>            
            <borrowDate>2013-7-1</borrowDate>
            <returnDate>2013-7-5</returnDate>
        </record>
    </book>
</books>

编辑

下面是我的 C# 代码和输出结果:

books books = new books {
        bookNum = 2,
        Books = new books.book[] { 
            new books.book {  
                name = "Book1", 
                records = new books.book.record[] {
                    new books.book.record {
                        borrowDate = "2013-1-3",
                        returnDate = "2013-1-5"
                    },
                     new books.book.record {
                        borrowDate = "2013-2-3",
                        returnDate = "2013-4-5"
                    }
                }
            },
             new books.book {  
                name = "Book1", 
                records = new books.book.record[] {
                    new books.book.record {
                        borrowDate = "2013-1-3",
                        returnDate = "2013-1-5"
                    },
                     new books.book.record {
                        borrowDate = "2013-2-3",
                        returnDate = "2013-4-5"
                    }
                }
            }
        }
    };


    XmlSerializer xsSubmit = new XmlSerializer(typeof(books));

    XmlDocument doc = new XmlDocument();

    System.IO.StringWriter sww = new System.IO.StringWriter();
    XmlWriter writer = XmlWriter.Create(sww);
    xsSubmit.Serialize(writer, books);
    var xml = sww.ToString(); // Your xml
    context.Response.Write(xml);

XML:

<books>
    <bookNum>2</bookNum>
    <Books>
        <book>
            <name>Book1</name>
            <records>
                <record>
                    <borrowDate>2013-1-3</borrowDate>
                    <returnDate>2013-1-5</returnDate>
                </record>
                <record>
                    <borrowDate>2013-2-3</borrowDate>
                    <returnDate>2013-4-5</returnDate>
                </record>
            </records>
        </book>
        <book>
            <name>Book1</name>
            <records>
                <record>
                    <borrowDate>2013-1-3</borrowDate>
                    <returnDate>2013-1-5</returnDate>
                </record>
                <record>
                    <borrowDate>2013-2-3</borrowDate>
                    <returnDate>2013-4-5</returnDate>
                </record>
            </records>
         </book>
    </Books>
</books>

【问题讨论】:

  • 发布 XMLserializer 的输出?
  • “结果和下面的xml不一样”——怎么“不一样”?你会得到什么? (旁注:您对 XML 中的日期使用不寻常的格式 - ISO8601 YYYY-MM-DD)
  • 如果你做一个 xml 序列化你不能得到你的自定义 xml
  • 其实Mac,你可以。您需要对属性进行属性化以更改其元素名称,但您可以。
  • 您可以使用 IXmlSerializable 属性的实现来获得此类输出 - 请参阅我的答案中的第三个摘要。

标签: c# xml linq


【解决方案1】:

您不能使用标准序列化工具对问题中的类进行序列化,以便它具有与 &lt;bookNum&gt; 节点处于同一级别的 &lt;book&gt; 条目。

当使用标准序列化工具保存的类时,&lt;book&gt; 节点的列表将始终嵌套到与&lt;bookNum&gt; 节点处于同一级别的单独数组节点book 类上的 records 数组字段同样关注。

要生成您想要的 XML 输出 - &lt;book&gt; 节点与 &lt;bookNum&gt; 节点处于同一级别 - 您必须在 books 类中实现 IXmlSerializable 接口以进行自定义序列化。要查看IXmlSerializable 实现的示例,请访问以下链接:StackOverflow answerCodeProject article

另一种解决方案将是 - 正如user Alexandr 在对我的回答的评论中所述 - 从 List&lt;book&gt; 类型继承您的 books 类,并在您的 book 类字段 records 继承的类类型来自List&lt;record&gt; 类型。

从您的问题中序列化类时,假设您分配了正确的XmlRoot, XmlElement, XmlArray and XmlArrayItem attributes,如下所示:

[XmlRoot("books")]
public class books
{
    [XmlElement("bookNum")]
    public int bookNum { get; set; }

    [XmlRoot("book")]
    public class book
    {
        [XmlElement("name")]
        public string name { get; set; }

        [XmlRoot("record")]
        public class record
        {
            [XmlElement("borrowDate")]
            public string borrowDate { get; set; }

            [XmlElement("returnDate")]
            public string returnDate { get; set; }
        }

        [XmlArray("borrowRecords")]
        [XmlArrayItem("record")]
        public record[] records { get; set; }
    }

    [XmlArray("booksList")]
    [XmlArrayItem("book")]
    public book[] books { get; set; }
}

你会得到 XML 输出如下:

<books>
    <bookNum>2</bookNum>
    <booksList>
        <book>
            <name>Book 1</name>
            <borrowRecords>
                <record>
                    <borrowDate>2013-1-3</borrowDate>
                    <returnDate>2013-1-5</returnDate>
                </record>
                <record>            
                    <borrowDate>2013-2-3</borrowDate>
                    <returnDate>2013-4-5</returnDate>
                </record>
            </borrowRecords>
        </book>
        <book>
            <name>Book 2</name>
            <borrowRecords>
                <record>
                    <borrowDate>2013-1-3</borrowDate>
                    <returnDate>2013-1-5</returnDate>
                </record>
                <record>            
                    <borrowDate>2013-2-3</borrowDate>
                    <returnDate>2013-4-5</returnDate>
                </record>
            </borrowRecords>
        </book>
    </booksList>
</books>

【讨论】:

  • 你确定吗?因为我想我不久前通过让根类(在这种情况下为books)扩展List&lt;book&gt;,然后使用XmlElement-attribute 对其应用属性...
  • @Alxandr 继承列表是另一种选择 - 它改变了问题的一些条件。 +1 好主意。
  • 我也猜想这在不从列表继承的情况下是可行的,只需提供一个Add 方法,但我不知道 XmlSerializer 的内部是如何工作的......
  • @Alxandr 用谷歌快速搜索后,我没有找到Add 方法的解决方案——我想出的只是IXmlSerializable 的不同变体。将感谢指向替代解决方案的链接。
  • 这只是基于Add 方法启用var a = new List&lt;string&gt;() { "a", "b", "c" }; 之类的事实的猜测另外,List&lt;T&gt; 似乎没有实现IXmlSerializable,尽管它可能只是一个据我所知,序列化构造函数。我只是想,由于 .NET 赋予了 Add 方法特殊的含义,也许 XmlSerializer 也是如此......
【解决方案2】:

我对您的课程代码进行了以下更改。我无法使用默认序列化程序复制 XML 序列化,因为它不会复制“记录”元素而不给它一个容器元素。

[System.Xml.Serialization.XmlRoot("books")]
public class books 
{
    public int bookNum { get; set; }
    public class book {
        public string name { get; set; }
        public class record {
            public string borrowDate { get; set; }
            public string returnDate { get; set; }
        }
        public record[] records { get; set; }
    }
    public book[] books { get; set; }
}

序列化这个给我以下输出

<books xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
  <bookNum>2</bookNum>
  <books>
    <book>
      <name>first</name>
      <records>
        <record>
          <borrowDate>19/07/2013 4:41:29 PM</borrowDate>
          <returnDate>19/07/2013 4:41:29 PM</returnDate>
        </record>
      </records>
    </book>
  </books>
</books>

使用此测试代码

books bks = new books();
bks.bookNum = 2;
bks.books = new books.book[]{ new books.book{name="first", records = new books.book.record[] {new books.book.record{borrowDate = DateTime.Now.ToString(), returnDate = DateTime.Now.ToString()}}}};

System.Xml.Serialization.XmlSerializer serializer = new System.Xml.Serialization.XmlSerializer(typeof(books));

XmlWriterSettings settings = new XmlWriterSettings();
settings.Encoding = new UnicodeEncoding(false, false); // no BOM in a .NET string
settings.Indent = true;
settings.OmitXmlDeclaration = true;

using(StringWriter textWriter = new StringWriter()) {
    using(XmlWriter xmlWriter = XmlWriter.Create(textWriter, settings)) {
        serializer.Serialize(xmlWriter, bks);
    }
    return textWriter.ToString(); //This is the output as a string
}

【讨论】:

  • 我们不能让 处于同一级别吗?
  • books 是 book 的容器元素。这就是它在重新序列化时知道它们属于同一个属性的方式。
【解决方案3】:

我意识到这已经晚了几年,但我已经能够通过使用 XmlElementAttribute 来实现您想要的结构。

我通过使用XSD.exe 从 xml 生成架构定义并从 xsd 文件生成 .Net 代码发现了这一点。据我所知,这适用于 .Net 3.5 到 4.6。

这是我使用的类定义:

public class books
{
    public int bookNum { get; set; }
    public class book {
        public string name { get; set; }
        public class record {
            public string borrowDate { get; set; }
            public string returnDate { get; set; }
        }
        [XmlElement("record")]
        public record[] records { get; set; }
    }
    [XmlElement("book")]
    public book[] allBooks { get; set; }
}

这里有一个LinqPad sn-p 说明了序列化/反序列化(基于 David Colwell 的代码 sn-p,感谢 BTW 关于如何排除 BOM 的提示,这正是我想要的):

books bks = new books();
books bks2 = null;
bks.bookNum = 2;
bks.allBooks = new books.book[] 
        { 
            new books.book {
                name="book 1", 
                records = new books.book.record[] {
                        new books.book.record{borrowDate = DateTime.Now.ToString(), returnDate = DateTime.Now.ToString()}
                    }
                },
            new books.book { 
                name="book 2", 
                records = new books.book.record[] { 
                        new books.book.record{borrowDate = DateTime.Now.ToString(), returnDate = DateTime.Now.ToString()}, 
                        new books.book.record{borrowDate = DateTime.Now.ToString(), returnDate = DateTime.Now.ToString()}}
                    },
        };
string xmlString;

System.Xml.Serialization.XmlSerializer serializer = new System.Xml.Serialization.XmlSerializer(typeof(books));

XmlWriterSettings settings = new XmlWriterSettings();
settings.Encoding = new UnicodeEncoding(false, false); // no BOM in a .NET string
settings.Indent = true;
settings.OmitXmlDeclaration = true;

XmlSerializerNamespaces ns = new XmlSerializerNamespaces();
// exclude xsi and xsd namespaces by adding the following:
ns.Add(string.Empty, string.Empty);

using(StringWriter textWriter = new StringWriter()) {
    using(XmlWriter xmlWriter = XmlWriter.Create(textWriter, settings)) {
        serializer.Serialize(xmlWriter, bks, ns);
    }
    xmlString = textWriter.ToString(); //This is the output as a string
}

xmlString.Dump();

// Deserialize the xml string now       
using ( TextReader reader = new StringReader(xmlString) ) {
    bks2 = ( books )serializer.Deserialize(reader);
}

bks2.Dump();

这个生成的XML可以在不实现IXmlSerializable的情况下进行序列化和反序列化,如:

<books>
  <bookNum>2</bookNum>
  <book>
    <name>book 1</name>
    <record>
      <borrowDate>2/2/2016 5:57:25 PM</borrowDate>
      <returnDate>2/2/2016 5:57:25 PM</returnDate>
    </record>
  </book>
  <book>
    <name>book 2</name>
    <record>
      <borrowDate>2/2/2016 5:57:25 PM</borrowDate>
      <returnDate>2/2/2016 5:57:25 PM</returnDate>
    </record>
    <record>
      <borrowDate>2/2/2016 5:57:25 PM</borrowDate>
      <returnDate>2/2/2016 5:57:25 PM</returnDate>
    </record>
  </book>
</books>

【讨论】:

  • 赞成使用 xsd.exe 从所需的 XML 创建类的想法!肯定会把它添加到我的工具包中。
【解决方案4】:

如果您需要其他类,例如 books 类中的 book2,您可以使用一些特殊说明来实现它。 例子

public class books
{     
   public int bookNum {get; set; }
   public class book {
         public string name {get; set; }
         public class record {
             public string borrowDate {get; set; }
             public string returnDate {get; set; }
         }
         [XmlElement ("record")]
         public record [] records {get; set; }
     }
     [XmlElement ("book")]
     public book [] allBooks {get; set; }

     public int book2Num {get; set; }
     public class book2 {
         public string name {get; set; }
         public class record {
             public string borrowDate {get; set; }
             public string returnDate {get; set; }
         }
         [XmlElement ("record")]
         public record [] records {get; set; }
     }
     [XmlElement ("book2")]
     public book2 [] allBook2 {get; set; }
}`

当我尝试运行程序时出现以下错误:

“附加信息:反映类型时出错”

【讨论】:

  • 请编辑答案以详细说明特殊说明的主题。
猜你喜欢
  • 2018-11-27
  • 2012-07-11
  • 2011-03-12
  • 1970-01-01
  • 2013-06-20
  • 2015-01-13
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多