为什么XmlSerializer的Deserialize()吐出一个子对象,这是一个XmlNode []?

时间:2012-11-22 05:04:17

标签: c# .net serialization xmlserializer

我正在使用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对象,其中Arguments为XmlNode[],第一项是赋予QueuedFile类型的属性,其他索引是属性的元素。但为什么不重新创建QueuedFile对象?

我怀疑这可能与C#名称空间有关,并且反序列化的引擎无法找到或使用QueuedFile ...但我不明白为什么我忘记了{{1它确保告诉我它没有预期XmlInclude()现在我已经添加了QueuedFile我没有错误,只是一个不完整的反序列化。

帮助?我已经阅读了所有可以阅读的内容,并将我所知道的所有内容用Google搜索到了Google并且卡住了。我当然有很多关于XML序列化的知识但是我不确定我是如何失败的应该是非常简单的事情(我实际上做了几乎完全像这样的事情没有任何问题,唯一的区别是一切都是在同一个C#名称空间中。

2 个答案:

答案 0 :(得分: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));
        }

答案 1 :(得分:1)

对于遇到类似问题的任何人,根据您的情况,您最好使用NetDataContractSerializer。它是DataContractSerializer的替代品,它可以在XML中记录.Net类型,使反序列化变得轻而易举,因为它确切地知道涉及哪些类型,因此您不需要使用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();
    }
}

简单就是馅饼!