我有一个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”。
答案 0 :(得分:3)
好的,我已经在C#中重新实现了您的解决方案,不同之处在于您没有在TaskRequests
类中为MobileRequest
提供命名空间。
总结:
1)您混合了DataContractSerializer
和XmlSerializer
的属性,这使得此代码难以管理,我只使用了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://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
的许多问题,尤其是命名空间的问题,所以如果可以的话,您可能希望使用本主题中建议的DataContractSerializer
或NetDataContractSerializer
:{ {3}}