【问题标题】:Deserializing List of objects yields empty list反序列化对象列表产生空列表
【发布时间】:2013-08-21 18:51:45
【问题描述】:

我有一个 RESTful WCF 服务,我正在尝试向其发送任务列表。我在其他问题上发现你必须封装列表,我已经完成了,序列化看起来很顺利。

序列化列表产生的 XML 如下所示:

<?xml version="1.0" encoding="utf-8"?>
<MobileRequest xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
  <TaskRequests xmlns="MyNameSpace">
    <MobileTask>
      <TaskArgs xsi:type="GetUser">
        <Password>test</Password>
        <UserName>t</UserName>
      </TaskArgs>
      <TaskID>1</TaskID>
      <TaskType>GetUser</TaskType>
    </MobileTask>
    <MobileTask>
      <TaskArgs xsi:type="GetUser">
        <Password>test</Password>
        <UserName>t2</UserName>
      </TaskArgs>
      <TaskID>2</TaskID>
      <TaskType>GetUser</TaskType>
    </MobileTask>
  </TaskRequests>
</MobileRequest>

所以MobileRequest 类有一个属性,它是MobileTask 对象的列表。 MobileRequest 类的代码非常简单,如下所示:

<Serializable(), DataContract(Name:="MobileRequest", [Namespace]:="MyNameSpace")> _
Public Class MobileRequest
    <XmlArray()> <DataMember(Name:="TaskRequests")> _
    Public TaskRequests As List(Of MobileTask) = New List(Of MobileTask)

    Public Sub New()
        Me.TaskRequests = New List(Of MobileTask)
    End Sub
End Class

MobileTask 类如下所示:

<Serializable(), DataContract(Name:="MobileTask", [Namespace]:="MyNamespace"), _
 KnownType(GetType(Obj.GetUser)), XmlInclude(GetType(Obj.GetUser))> _
Public Class MobileTask
    Public Enum TypesOfTasks As Integer
        Unknown = 0
        GetUser = 1
    End Enum

    <DataMember(Name:="TaskID")> _
    Public TaskID As Integer
    <DataMember(Name:="TaskType")> _
    Public TaskType As TypesOfTasks
    <DataMember(Name:="TaskArgs")> _
    Public TaskArgs As Object
End Class

TaskArgs 的类型根据 TaskType 而变化。我为 TaskArgs 可能是的每种可能类型都包含了一个 XMLInclude 和 KnownType 标记。 (这可能有点矫枉过正,但是当序列化的某些东西不起作用并且还没有尝试清理它时,我就这样做了)

我在我的测试应用程序中用来尝试反序列化 XML 的代码是:

Using logg As New System.IO.MemoryStream()
    Using sw As New System.IO.StreamWriter(logg)
        sw.Write(txtSource.Text)
        sw.Flush()
        If logg.Length > 0 Then
            Using reader As New System.IO.StreamReader(logg)
                logg.Position = 0
                Dim ser As New System.Xml.Serialization.XmlSerializer(GetType(SMS_VendorObj.MobileRequest)) 
                Dim results = ser.Deserialize(logg)
            End Using
        End If
    End Using
End Using

这是来自一个简单的 winform 设置,它允许我粘贴 XML,因此 txtSource 只是一个文本框,我将我从序列化中获得的 XML 放入其中。

在运行测试应用程序时,我可以逐步执行上述反序列化代码,在 Dim results = ser.Deserialize(logg) 行之后 results 是正确的类型,但 MobileTask 列表不包含任何项目。

是我在类文件中遗漏了什么,还是我尝试反序列化列表的方式有问题?

如果我错过了重要的代码,请告诉我,我会更新任何需要的内容。我还删掉了多余的部分,所以我可能在不知不觉中剪掉了太多。

编辑: GetUser 类:

<Serializable(), DataContract([Namespace]:="MyNamespace")> _
Public Class GetUser

    <DataMember(Name:="UserName")> _
    Public UserName As String
    <DataMember(Name:="Password")> _
    Public Password As String
End Class

编辑#2: 在处理了反序列化程序可能抛出的错误后,我发现反序列化过程在第 21 行位置 3 上抛出了一个UnknownNode 异常,然后是一个UnknownElement 异常。详细信息表明预期的元素是“:TaskRequests”。

【问题讨论】:

  • 抱歉,没有注意到您已经有 XmlInclude。你能显示类型 Obj.GetUser 的定义吗?
  • @BartoszKP 请参考编辑。
  • 您是否尝试过将XmlInclude 属性(KnownType 在这里无关,因为它指的是DataContractSerializer)到属性TaskArgs
  • @BartoszKP 您能否将您的建议应用于我发布的课程的答案?我尝试只是简单地将标签拖放到DataMember 标签之后,环境根本不喜欢这样,所以我不确定你的意思。

标签: xml vb.net serialization


【解决方案1】:

好的,我已经用 C# 重新实现了您的解决方案,不同之处在于您没有在 MobileRequest 类中为 TaskRequests 提供命名空间。

总结一下:

1) 你混合了DataContractSerializerXmlSerializer 的属性,这使得这段代码难以管理,我只使用了XmlSerializer 相关的属性。

2) 您提供的 XML 不能是序列化您提供的类的结果。它在 TaskReqests 字段上包含“MyNameSpace”,而在您的代码中,XmlSerializer 没有命名空间(仅适用于 DataContractSerializer,这与此处无关)。

3) 使您给出的 XML 反序列化 正确 的代码的最小修复是:

[Serializable]
public class MobileRequest
{
    [XmlArray(Namespace="MyNameSpace")] //note the namespace
    public List<MobileTask> TaskRequests = new List<MobileTask>();

    public MobileRequest()
    {
        TaskRequests = new List<MobileTask>();
    }
}

(为了清楚起见,我还删除了DataContractSerializer 属性)。 它在 C# 中,但我希望您能轻松发现差异 - 将 Namespace 参数赋予 XmlArray 属性。

可能有用的参考资料:

http://msdn.microsoft.com/en-us/library/system.xml.serialization.xmlserializer.aspx

http://msdn.microsoft.com/en-us/library/system.runtime.serialization.datacontractserializer%28v=vs.90%29.aspx

http://www.codeproject.com/Articles/14064/Using-the-XmlSerializer-Attributes

编辑:

事实证明,XmlSerializer 无法反序列化它自己序列化的内容!在以下代码之后使用上面定义的类和描述的修复:

XmlSerializer s = new XmlSerializer(typeof(MobileRequest));

MobileRequest mr = new MobileRequest();
mr.TaskRequests.Add(new MobileTask() { TaskID = 1, TaskType = MobileTask.TypesOfTasks.GetUser, TaskArgs = new Obj.GetUser() { Password = "test", UserName = "t" } });
mr.TaskRequests.Add(new MobileTask() { TaskID = 2, TaskType = MobileTask.TypesOfTasks.GetUser, TaskArgs = new Obj.GetUser() { Password = "test", UserName = "t2" } });

using (StreamWriter sw = new StreamWriter("test.txt"))
{
  s.Serialize(sw, mr);
}

StreamReader sr = new StreamReader("test.txt");
XmlReader xmlReader = XmlReader.Create(sr);
XmlDeserializationEvents xde = new XmlDeserializationEvents();
xde.OnUnknownElement = new XmlElementEventHandler((o, e) =>
{
  Console.WriteLine("Unknown element:" + e.Element.Name);
});
xde.OnUnknownNode = new XmlNodeEventHandler((o, e) =>
  {
    Console.WriteLine("Unknown node:" + e.Name);
  });
var r = s.Deserialize(xmlReader, xde);
Console.ReadKey();

XmlNodes 出现而不是 TaskArgs!删除所有命名空间定义和引用可以解决问题。我读过很多关于XmlSerializer 的问题,尤其是命名空间的问题,所以如果可以的话,你可能会想要使用DataContractSerializerNetDataContractSerializer,正如本主题中所建议的那样:Why is XmlSerializer's Deserialize() spitting out a child object which is a XmlNode[]?

【讨论】:

  • 环境一直说“属性 'XmlIncluedAttribute' 不能应用于 'TaskArgs',因为该属性在此声明中无效”。但是,当我浏览您链接的文档时,我注意到您可以处理未知的元素/节点/属性事件,以获取有关反序列化失败原因的更多信息。我将尝试获取更多信息并根据我的发现更新问题。
  • 我阅读了更多的文档,并将类上的 XMLInclude 标记切换为属性上的 XMLElement 标记。我还能够找到有关实际错误的更多信息并更新了问题。
  • 好的,这似乎已经解决了这个特定问题,但现在不是 TaskArgs 对象被反序列化为我期望它被反序列化为 XMLNodes 数组的任何类。你知道为什么会这样吗?
  • 我已经设法让它只在没有命名空间的情况下工作。我已经编辑了答案,并建议了其他序列化程序——在这个序列化程序上,我放弃了:)
  • @BatoszKP:我也放弃了。我整个星期都在与它作斗争,但没有取得太大进展。我能够做的是将TaskArgs 属性更改为一个字符串,并使用它来保存我试图拥有serializer 句柄的对象的序列化版本。这需要我将serialize/deserialize 步骤添加到每条消息的发送和接收中,但这使得TaskArgs 对象值符合我的期望。不完全理想,但我想它可以工作。
猜你喜欢
  • 2014-12-19
  • 1970-01-01
  • 2020-10-31
  • 2020-11-06
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-10-10
  • 1970-01-01
相关资源
最近更新 更多