反序列化对象列表会产生空列表

时间:2013-08-21 18:51:46

标签: xml vb.net serialization

我有一个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”。

1 个答案:

答案 0 :(得分:3)

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

总结:

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

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

3)使用反序列化正确的代码的最小修复程序(参见下面的编辑)是:

[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:{ {3}}