XML序列化中反序列化数组时出现了"Element was not expected"的错误。

3

好的。我正在尝试与Pivotal Tracker API通信,该API仅以XML格式返回数据。我有以下XML尝试反序列化为我的领域模型。


<?xml version="1.0" encoding="UTF-8"?>
<stories type="array" count="2" total="2">
  <story>
    <id type="integer">2909137</id>
    <project_id type="integer">68153</project_id>
    <story_type>bug</story_type>
    <url>http://www.pivotaltracker.com/story/show/2909137</url>
    <current_state>unscheduled</current_state>
    <description></description>
    <name>Test #2</name>
    <requested_by>Anthony Shaw</requested_by>
    <created_at type="datetime">2010/03/23 20:05:58 EDT</created_at>
    <updated_at type="datetime">2010/03/23 20:05:58 EDT</updated_at>
  </story>
  <story>
    <id type="integer">2909135</id>
    <project_id type="integer">68153</project_id>
    <story_type>feature</story_type>
    <url>http://www.pivotaltracker.com/story/show/2909135</url>
    <estimate type="integer">-1</estimate>
    <current_state>unscheduled</current_state>
    <description></description>
    <name>Test #1</name>
    <requested_by>Anthony Shaw</requested_by>
    <created_at type="datetime">2010/03/23 20:05:53 EDT</created_at>
    <updated_at type="datetime">2010/03/23 20:05:53 EDT</updated_at>
  </story>
</stories>

我的“故事”对象创建如下:


public class story
{
     public int id { get; set; }
     public int estimate { get; set; }
     public int project_id { get; set; }

        public string story_type { get; set; }
        public string url { get; set; }
        public string current_state { get; set; }
        public string description { get; set; }
        public string name { get; set; }
        public string requested_by { get; set; }
        public string labels { get; set; }
        public string lighthouse_id { get; set; }
        public string lighthouse_url { get; set; }
        public string owned_by { get; set; }
        public string accepted_at { get; set; }
        public string created_at { get; set; }

        public attachment[] attachments { get; set; }
        public note[] notes { get; set; }
    }

当我执行反序列化代码时,我收到以下异常:

Exception:
   There is an error in XML document (2, 2).

Inner Exception:
   <stories xmlns=''> was not expected.

我可以成功反序列化每个故事,但我无法将此xml反序列化为“故事”对象数组。
我的反序列化代码(value是xml字符串)。

var byteArray = Encoding.ASCII.GetBytes(value);
var stream = new MemoryStream(byteArray);
var deserializedObject = new XmlSerializer(typeof (story[])).Deserialize(stream)

有人有任何想法吗?


1
Stack Overflow没有像Wikipedia那样的冲突检测机制。如果您同时进行编辑,最近的编辑将会覆盖较早的编辑并且删除所有先前的编辑内容。 - C. K. Young
5个回答

9

我可以提供一种更为简洁的解决方案。将反序列化设置成以下形式:

var deserializedObject = new XmlSerializer(typeof(story[]), new XmlRootAttribute("stories")).Deserialize(stream);

通过在XmlSerializer中指定第二个参数,您可以避免不得不存根该类。它让序列化程序知道根元素的名称。
为了使其工作,表示数组元素类型的类的名称必须与XML名称完全匹配,例如class story {}<story>。您可以通过指定XmlType来解决这个问题(我建议将其作为最佳实践)。
[XmlType("story")]
public class Story
{
...
}

我更喜欢这样做,因为它使我不再被 XML 类型名称所束缚。


这对我很有帮助,我认为这是一种更整洁的做事方式。 - ChadT
+1 我忘记了 XmlSerializer() 的 2-arg 版本,这个答案简洁明了。 - codenheim
我也同意使用XmlType(),我喜欢在大多数需要被人类读取/编辑的序列化中使用它来将元素转换为小写,因为我发现在标签混乱的情况下查找数据时,与标准的.NET CamelCase / CamelCaps相比,这样更容易阅读。 - codenheim

4
问题在于您没有名为“stories”的属性。当XML序列化程序看到它时,不知道该怎么处理stories元素。
您可以尝试创建一个“stories”类:
public class stories : List<story> {}

并使用

var byteArray = Encoding.ASCII.GetBytes(value);
stories deserializedObject  = null;
using (var stream = new MemoryStream(byteArray))
{
    var storiesSerializer = new XmlSerializer(typeof (stories));
    deserializedObject = (stories)storiesSerializer .Deserialize(stream);
}

我尝试了一下,因为我喜欢你的实现方式,它最好地继承了List<story>,但是它没有起作用。不过还是谢谢你尝试了 :) - Anthony Shaw
您可以避免为数组创建单独的类。请参见下面的示例。 - luke

2
尝试类似这样的东西:
public class stories
{
    [XmlElement("story")]
    public story[] storyarray { get; set; }
}

...

var byteArray = Encoding.ASCII.GetBytes(value);
XmlSerializer serializer = new XmlSerializer(typeof(stories));
stories myStories = null;

using (var stream = new MemoryStream(byteArray))
{
    myStories = (stories)serializer.Deserialize(stream);
}

foreach (story stor in myStories.storyarray)
    Console.WriteLine(stor.story_type);

编辑:根据反馈更新代码示例,使用了using语句。


@Anthony Shaw:一定要像我这样将MemoryStream放入using块中。否则,如果抛出异常,您将泄漏资源。 - John Saunders
@AnthonyPegram:谢谢,但您还需要 myStories = (stories) serialiser.Deserialize(stream) - John Saunders
好眼力,约翰。我没有想到对原始代码进行那种调整。唉!这就是我在编辑器中编辑代码而不是在IDE中的后果! - Anthony Pegram
你不必费心去存根仅包含数组的根类。我在下面发布了一个示例。 - luke

1

XMSerializer期望一个XML命名空间来理解您的XML。

xmlns="http://schemas.microsoft.com"

... 应该做。请参见this页面底部的XML示例。


我所拥有的XML是由Pivotal Tracker API的RESTful服务生成的。我想,如果必须的话,我可以编辑它,但我宁愿不这样做,因为我正在使用反序列化方法来处理多个类,而不仅仅是故事。 - Anthony Shaw
老问题了,但我必须在这里尊重地发表评论。这个答案是不正确的。XML解析器不需要或使用命名空间来解析或理解您的XML,它们只用于为元素声明唯一的命名空间。否则,如果混合模式/类型,则元素名称可能会冲突。反序列化就是解析。即使我们谈论XML验证,也会查看XSD,而不是xmlns。您可以决定为命名空间使用任何URI,假设它是唯一的。一些公司使用其URI作为其命名空间的事实仅仅是一个产物。 - codenheim

0

我建议您从Web服务获取一些示例XML并生成XSD。然后,使用该XSD,您可以生成具有所有正确序列化属性的类。

  1. 要生成模式(除非您想编写自己的代码),请在Visual Studio中打开示例XML文件,并选择XML -> 创建模式菜单选项。保存该XSD。
  2. 要生成类,请从VS命令提示符中运行XSD命令。如果不带参数运行它,它将显示您可以使用的命令行参数。
  3. 现在您可以创建类型安全的XML序列化器。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接