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对象,其中 Arguments 是XmlNode[]第一项是属性,将 QueuedFile 作为类型,其他索引是属性的元素。但是为什么没有QueuedFile重新创建对象?

我怀疑这可能与 C# 命名空间有关,并且进行反序列化的引擎无法找到或使用QueuedFile...现在我已经添加了我没有错误,只是一个不完整的反序列化。XmlInclude()QueuedFileXmlInclude()

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

4

2 回答 2

1

对于遇到类似问题的任何人,根据您的情况,使用 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();
    }
}

非常简单!

于 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 回答