1

XmlSerializer単純なオブジェクトをシリアル化してから逆シリアル化するために使用しています。驚いたことにオブジェクトを逆シリアル化すると、子オブジェクトが適切に逆シリアル化されておらず、代わりにになっていることがわかりましたXmlNode[]

これが私が持っている構造に非常に近いものです:

// This line I put in here as a way of sneaking into the XML the 
// root node's C# namespace, since it's not the same as the 
// deserializing code and the deserializing code seemed unable to 
// deserialize properly without knowing the Type (see my code below).
// So I basically just use this fake construct to get the namespace 
// and make a Type of it to feed the XmlSerializer() instantiation.
[XmlRoot(Namespace = "http://foo.com/CSharpNamespace/Foo.Bar")]   

// This is because QueuedFile can be given to the Argument array.
[XmlInclude(typeof(QueuedFile))]
// This class is Foo.Bar.CommandAndArguments
public class CommandAndArguments {
    public String Command;
    public object[] Arguments;
}

// I don't think this matters to XmlSerialize, but just in case...
[Serializable()] 

// I added this line just thinking maybe it would help, but it doesn't
// do anything.  I tried it without the XmlType first, and that
// didn't work.
[XmlType("Foo.Baz.Bat.QueuedFile")]

// This class is Foo.Baz.Bat.QueuedFile (in a different c# 
// namespace than CommandAndArguments and the deserializing code)
public QueuedFile {
    public String FileName;
    public String DirectoryName;
}

そして、それを逆シリアル化するコードは次のようになります。

public static object DeserializeXml(String objectToDeserialize)
        {
            String rootNodeName = "";
            String rootNodeNamespace = "";

            using (XmlReader xmlReader = XmlReader.Create(new StringReader(objectToDeserialize)))
            {
                if (xmlReader.MoveToContent() == XmlNodeType.Element)
                {
                    rootNodeName = xmlReader.Name;
                    rootNodeNamespace = xmlReader.NamespaceURI;

                    if (rootNodeNamespace.StartsWith("http://foo.com/CSharpNamespace/"))
                    {
                        rootNodeName = rootNodeNamespace.Substring("http://foo.com/CSharpNamespace/".Length) + "." +
                                       rootNodeName;
                    }
                }
            }

            //MessageBox.Show(rootNodeName);
            try
            {
                Type t = DetermineTypeFromName(rootNodeName);

                if (t == null)
                {
                    throw new Exception("Could not determine type of serialized string.  Type listed as: "+rootNodeName);                    
                }

                var s = new XmlSerializer(t);
                return s.Deserialize(new StringReader(objectToDeserialize));

                // object o = new object();
                // MethodInfo castMethod = o.GetType().GetMethod("Cast").MakeGenericMethod(t);
                // return castMethod.Invoke(null, new object[] { s.Deserialize(new StringReader(objectToDeserialize)) });
            }
            catch (InvalidOperationException)
            {
                return null;
            }
        }

そして、CommandAndArgumentsシリアル化されたときのXMLは次のとおりです。

<?xml version="1.0" encoding="utf-16"?>
<CommandAndArguments xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns="http://foo.com/CSharpNamespace/Foo.Bar">
  <Command>I am a command</Command>
  <Arguments>
    <anyType xsi:type="Foo.Baz.Bat.QueuedFile">
      <FileName xmlns="">HelloWorld.txt</FileName>
      <DirectoryName xmlns="">C:\foo\bar</DirectoryName>
    </anyType>
  </Arguments>
</CommandAndArguments>

しかし、逆シリアル化するとCommandAndArguments、引数がオブジェクトでありXmlNode[]、最初の項目が属性であり、QueuedFileをタイプとして指定し、他のインデックスがプロパティの要素であるオブジェクトが与えられます。しかし、なぜQueuedFileオブジェクトが再作成されなかったのですか?

これは、C#名前空間と、逆シリアル化を実行しているエンジンが検索または操作できないことに関係しているのQueuedFileではないかと思います...しかし、忘れたときに、予期しXmlInclude()ていなかったことを確認したので、理由はわかりません。QueuedFile追加したXmlInclude()ので、エラーは発生せず、逆シリアル化が不完全です。

ヘルプ?私は私が読むために見つけることができるすべてを読み、私が知っているすべてをグーグルにグーグルで検索し、行き詰まっています。私は確かにXMLシリアル化について学ぶことがたくさんありますが、かなり単純なはずの何かでどのように失敗しているのかわかりません(実際には、以前は問題なくほぼ同じようなことをしましたが、唯一の違いはすべてが同じC#名前空間内)。

4

2 に答える 2

1

同様の問題を抱えている人にとっては、状況によっては、NetDataContractSerializerを使用したほうがよいでしょう。これは、XMLに.Netタイプを記録してデシリアライズを簡単にするDataContractSerializerの代替手段です。これは、関係するタイプを正確に認識しているため、deserializeコマンドでルートオブジェクトのタイプを指定する必要がないためです。また、XMLまたはバイナリ形式で出力を生成できます(デバッグを容易にするためにXMLを使用します)。

文字列との間でオブジェクトを簡単にシリアル化および逆シリアル化するためのサンプルコードを次に示します。

private static object Deserialize(string xml)
{
    object toReturn = null;

    using (Stream stream = new MemoryStream())
    {
        byte[] data = System.Text.Encoding.UTF8.GetBytes(xml);
        stream.Write(data, 0, data.Length);
        stream.Position = 0;
        var netDataContractSerializer = new NetDataContractSerializer();

        toReturn = netDataContractSerializer.Deserialize(stream);
    }

    return toReturn;
}

private static string Serialize(object obj)
{
    using (var memoryStream = new MemoryStream())
    using (var reader = new StreamReader(memoryStream))
    {
        var netDataContractSerializer = new NetDataContractSerializer();
        netDataContractSerializer.Serialize(memoryStream, obj);

        memoryStream.Position = 0;
        return reader.ReadToEnd();
    }
}

やさしい!

于 2012-11-24T01:06:59.833 に答える
1

XML形式を変更できますか、それとも修正されていますか?何が問題なのかわかりませんが、DataContractSerializerクラスを問題なく幅広く使用しています。

http://msdn.microsoft.com/en-us/library/system.runtime.serialization.datacontractserializer.aspx

public static void WriteObject(string fileName)
        {
            Console.WriteLine(
                "Creating a Person object and serializing it.");
            Person p1 = new Person("Zighetti", "Barbara", 101);
            FileStream writer = new FileStream(fileName, FileMode.Create);
            DataContractSerializer ser =
                new DataContractSerializer(typeof(Person));
            ser.WriteObject(writer, p1);
            writer.Close();
        }

        public static void ReadObject(string fileName)
        {
            Console.WriteLine("Deserializing an instance of the object.");
            FileStream fs = new FileStream(fileName,
            FileMode.Open);
            XmlDictionaryReader reader =
                XmlDictionaryReader.CreateTextReader(fs, new XmlDictionaryReaderQuotas());
            DataContractSerializer ser = new DataContractSerializer(typeof(Person));

            // Deserialize the data and read it from the instance.
            Person deserializedPerson =
                (Person)ser.ReadObject(reader, true);
            reader.Close();
            fs.Close();
            Console.WriteLine(String.Format("{0} {1}, ID: {2}",
            deserializedPerson.FirstName, deserializedPerson.LastName,
            deserializedPerson.ID));
        }
于 2012-11-22T06:16:16.843 に答える