【问题标题】:Why is XmlSerializer's Deserialize() spitting out a child object which is a XmlNode[]?为什么 XmlSerializer 的 Deserialize() 会吐出一个 XmlNode[] 的子对象?
【发布时间】:2012-11-10 11:13:27
【问题描述】:

我正在使用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... 但我不明白为什么,因为当我忘记了 XmlInclude() 时一定要告诉我它没想到QueuedFile,现在我已经添加了XmlInclude(),我没有收到任何错误,只是反序列化不完整。

帮助?我已经阅读了我能找到的所有内容,并在谷歌上搜索了我所知道的所有内容并被卡住了。我当然有很多关于 XML 序列化的知识要学习,但我不确定我在应该很简单的事情上是如何失败的(实际上我之前做过几乎完全一样的事情,没有任何问题,唯一的区别是一切都是在同一个 C# 命名空间中)。

【问题讨论】:

  • 我研究了我认为您所描述的内容的变体,基本上只是试图从 XmlNode[] 重构对象,虽然我确信它可以完成并且可能不会很难做到我找不到任何我认为可能很常见的现有示例(因为 XmlSerializer 必须为其他人吐出这个)。我决定暂时不尝试编写自己的代码,因为正如您所说,这感觉很骇人听闻,而且我最好尝试弄清楚我在概念上做错了什么来惹恼XmlSerializer。感谢您的建议!

标签: c# .net serialization xmlserializer


【解决方案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));
        }

【讨论】:

  • 我会尽快调查此事(今天是美国假期,所以家人要过来)。我可以将 XML 的格式更改为任何我想要的格式。我以前没有尝试过数据合同的东西,但看起来还不错。谢谢!我会让你知道结果如何。
  • 根据您的建议,我研究了 DataContractSerializer,虽然它可能解决了我遇到的问题,但我意识到它的表亲 NetDataContractSerializer 实际上一直是我真正想要的。 NetDataContractSerializer 的主要优点是将 C# 类型耦合到数据协定的紧密性,这意味着它知道我的对象的命名空间,我不需要告诉它我正在反序列化的对象类型,它已经知道了。我只是想要一些能够吐出 XML 以便于调试的东西,而这也是如此!谢谢!
【解决方案2】:

对于遇到类似问题的任何人,根据您的情况,使用 NetDataContractSerializer 可能会更好。它是 DataContractSerializer 的替代方案,它在 XML 中记录 .Net 类型,使反序列化变得轻而易举,因为它确切地知道所涉及的类型,因此您无需使用反序列化命令告诉它根对象的类型。它可以生成 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();
    }
}

简单易行!

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2022-01-15
    • 2013-05-27
    • 2010-09-22
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多