2

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

4

1 回答 1

3

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

总结一下:

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

2) 您提供的 XML不能是序列化您提供的类的结果。它在字段上包含“MyNameSpace” TaskReqests,而在您的代码中没有名称空间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按照本主题中的建议:为什么 XmlSerializer 的 Deserialize() 会吐出一个 XmlNode [ ]?.

于 2013-08-21T19:06:29.577 回答