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オブジェクトのリストである 1 つのプロパティがあります。クラスのコード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

これは、XML を貼り付けることができる単純な winform セットアップからのものであるため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:デシリアライザー がスローする可能性のあるエラーを処理した後、デシリアライゼーション プロセスがUnknownNode例外をスローし、その後にUnknownElement21 行目の位置 3 で例外がスローされていることがわかりました。

4

1 に答える 1

3

わかりました、C# でソリューションを再実装しました。違いは、クラスTaskRequests内の名前空間を提供しなかったことです。MobileRequest

要約する:

DataContractSerializer1)との属性を混在さXmlSerializerせると、このコードの管理が難しくなりXmlSerializerます。関連する属性のみを使用しました。

2) 提供した XML は、提示したクラスをシリアル化した結果であってはなりませんフィールドに「MyNameSpace」が含まれていTaskReqestsますが、コードには名前空間がありませんXmlSerializerDataContractSerializerここでは関係ありません)。

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();

XmlNodesTaskArgs の代わりに表示されます! 名前空間の定義と参照をすべて削除すると、問題が解決します。XmlSerializerに関する多くの問題、特に名前空間の問題について読んだことがあります。そのため、可能であれば、このトピックで提案されているようにDataContractSerializerまたはを使用することをお勧めします。 ]? .NetDataContractSerializer

于 2013-08-21T19:06:29.577 に答える