【问题标题】:(De)Serializing "Object"s(De)序列化“对象”
【发布时间】:2011-09-29 13:20:10
【问题描述】:

我必须对以下类进行 XML(反)序列化:

这给出了以下输出:

<ArrayOfPropertyFilter xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
                       xmlns:xsd="http://www.w3.org/2001/XMLSchema">
  <PropertyFilter>
    <AndOr>And</AndOr>
    <LeftBracket>None</LeftBracket>
    <Property>17</Property>
    <Operator>Equal</Operator>
    <Value xsi:type="xsd:string">lll</Value>
    <RightBracket>None</RightBracket>
  </PropertyFilter>
</ArrayOfPropertyFilter>

并且,在反序列化之后它给出

我怎样才能“告诉”序列化程序保持值“原样”,没有任何 XML 节点....(在具体情况下,值应该是“lll”而不是包含文本“lll”的 XMLNode)?

编辑

Bellow 是一个完整的 C# 示例。输出是

值为 = 'System.Xml.XmlNode[]'

using System;
using System.IO;
using System.Text;
using System.Windows.Forms;
using System.Xml;
using System.Xml.Serialization;

namespace WindowsFormsApplication13
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
        }

        private void Form1_Load(object sender, EventArgs e)
        {
            PropertyFilter filter = new PropertyFilter();
            filter.AndOr = "Jora";
            filter.Value = "haha";
            filter.Property = 15;

            var xml = filter.SerializeToString();
            XmlDocument xmlDoc = new XmlDocument();
            xmlDoc.LoadXml(xml);

            PropertyFilter cloneFilter = xmlDoc.Deserialize<PropertyFilter>();

            Console.WriteLine("Value is = '{0}'", cloneFilter.Value);
        }
    }

    public class PropertyFilter
    {
        public string AndOr { get; set; }
        public string LeftBracket { get; set; }
        public int Property { get; set; }
        public string Operator { get; set; }
        public object Value { get; set; }
        public string RightBracket { get; set; }
    }

    public static class Utils
    {
        public static string SerializeToString(this object instance)
        {
            if (instance == null)
                throw new ArgumentNullException("instance");
            StringBuilder sb = new StringBuilder();
            StringWriter sw = new StringWriter(sb);
            XmlSerializer serializer = new XmlSerializer(
                instance.GetType(), null, new Type[0], null, null);
            serializer.Serialize(sw, instance);
            return sb.ToString();
        }

        public static T Deserialize<T>(this XmlDocument xmlDoc)
        {
            XmlNodeReader reader = new XmlNodeReader(xmlDoc.DocumentElement);
            XmlSerializer serializer = new XmlSerializer(typeof(T));
            object obj = serializer.Deserialize(reader);
            T myT = (T)obj;
            return myT;
        }

    }
}

编辑 2

为了强调安东的答案,第二个例子(更新了 Groo 的评论):

using System;
using System.IO;
using System.Text;
using System.Windows.Forms;
using System.Xml;
using System.Xml.Serialization;

namespace WindowsFormsApplication13
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
        }

        private void Form1_Load(object sender, EventArgs e)
        {
            PropertyFilter filter = new PropertyFilter();
            filter.AndOr = "Jora";
            var obj = new Hehe();
            obj.Behehe = 4526;
            filter.Value = obj;
            filter.Property = 15;

            var xml = filter.SerializeToString();
            PropertyFilter cloneFilter = xml.Deserialize<PropertyFilter>();

            Console.WriteLine("Value is = '{0}'", cloneFilter.Value);
        }
    }

    public class Hehe
    {
        public int Behehe { get; set; }
        public override string ToString()
        {
            return string.Format("behehe is '{0}'", Behehe);
        }
    }

    public class PropertyFilter
    {
        public string AndOr { get; set; }
        public string LeftBracket { get; set; }
        public int Property { get; set; }
        public string Operator { get; set; }
        //[XmlElement(typeof(Hehe))]
        public object Value { get; set; }
        public string RightBracket { get; set; }
    }

    public static class Utils
    {
        public static string SerializeToString(this object instance)
        {
            if (instance == null)
                throw new ArgumentNullException("instance");
            StringBuilder sb = new StringBuilder();

            XmlSerializer serializer = new XmlSerializer(instance.GetType(), null, new Type[0], null, null);
            using (StringWriter sw = new StringWriter(sb))
            {
                serializer.Serialize(sw, instance);
            }
            return sb.ToString();
        }

        public static T Deserialize<T>(this string xmlString)
        {
            XmlSerializer serializer = new XmlSerializer(typeof(T));
            using (StringReader sr = new StringReader(xmlString))
            {
                return (T)serializer.Deserialize(sr);
            }
        }

    }
}

【问题讨论】:

  • 您期望的 XML 是什么?
  • @Seb 正如我所提到的,我希望我的反序列化值是对象(字符串)而不是 XmlNode。我演示Xml格式只是为了好奇,我不关心Xml,而是关心反序列化的值......
  • 如果这个属性只包含字符串,那你为什么不把它的类型改成string呢?但是如果它可以包含 any object,那么你显然需要将类型信息存储在某个地方。
  • @serhio:任何想要将您的 xml 反序列化为真正的 CLR 对象的代码都需要知道对象的确切类型才能创建它。这就是XmlSerializer 创建一个包含此信息的XmlNode 的原因(在您的情况下,它有一个值为"xsd:string" 的xsi:type 属性-这是您的对象的类型)。因此,无论如何,您都需要将此类型信息保存在某处,以便以后能够检索。正如@Seb 和我自己指出的那样,如果您想以某种特定方式存储此 type 信息,请在您的问题中进行描述。
  • @serhio:我觉得我们彼此不理解。你能回答这个问题吗:想象一下我刚刚给你发送了一个包含以下文本的 XML 文件:&lt;Value&gt;10d&lt;/Value&gt;。您启动应用程序并将文件反序列化为对象。 反序列化后您的 Value 属性将是什么类型,为什么?

标签: .net serialization xml-serialization


【解决方案1】:

好的,这是有道理的,您使用 XmlDocument 进行反序列化。只需使用String(或任何流阅读器,如@Seb already pointed out),它就可以工作:

public static class Utils
{
    public static string SerializeToString(this object instance)
    {
        if (instance == null)
            throw new ArgumentNullException("instance");

        StringBuilder sb = new StringBuilder();
        XmlSerializer serializer = new XmlSerializer(instance.GetType());

        using (StringWriter sw = new StringWriter(sb))
        {
            serializer.Serialize(sw, instance);
        }

        return sb.ToString();
    }

    public static T Deserialize<T>(this string xmlString)
    {
        XmlSerializer serializer = new XmlSerializer(typeof(T));
        using (StringReader sr = new StringReader(xmlString))
        {
            return (T)serializer.Deserialize(sr);
        }
    }
}

用法:

// serialize
var xml = filter.SerializeToString();

// deserialize
var cloneFilter = xml.Deserialize<PropertyFilter>();

[编辑]

另外,附带说明:永远不要忘记处置实现IDisposable 的对象。这就是为任何实例化的Stream 添加using 构造的原因。

[编辑2]

正如 Anton 所说,在这种情况下,您需要明确指定额外的类型,因为 XmlSerializer 不会搜索所有可能的类型来找到匹配的类。

稍微更好的解决方案可能是使用接受这些类型的XmlSerializer 重载(这样您就不需要手动添加属性):

public static T Deserialize<T>(this string xmlString)
{
    Type[] typesToInclude = GetAllPossibleTypes();

    XmlSerializer serializer = new XmlSerializer(typeof(T), typesToInclude);
    using (StringReader sr = new StringReader(xmlString))
    {
        return (T)serializer.Deserialize(sr);
    }
}

这可以在应用程序启动时完成一次,但您确实需要提供适当的程序集(或多个程序集)以确保涵盖所有可能的类型:

public static class Utils
{
    private static readonly Type[] _typesToInclude = GetPossibleUserTypes();

    private static Type[] GetPossibleUserTypes()
    {
        // this part should be changed to load types from the assembly
        // that contains your potential Value candidates
        Assembly assembly = Assembly.GetAssembly(typeof(PropertyFilter));

        // get public classes only
        return assembly.GetTypes().Where(t => t.IsPublic && !t.IsAbstract).ToArray();
    }

    public static string SerializeToString(this object instance)
    {
        if (instance == null)
            throw new ArgumentNullException("instance");

        var sb = new StringBuilder();
        var serializer = new XmlSerializer(instance.GetType(), _typesToInclude);

        using (StringWriter sw = new StringWriter(sb))
        {
            serializer.Serialize(sw, instance);
        }

        return sb.ToString();
    }

    public static T Deserialize<T>(this string xmlString)
    {
        var serializer = new XmlSerializer(typeof(T), _typesToInclude);
        using (StringReader sr = new StringReader(xmlString))
        {
            return (T)serializer.Deserialize(sr);
        }
    }
}

【讨论】:

  • 公平地说,这是@Seb 实际上首先写的,所以我相信这归功于他/她。
  • 感谢您的评论。但 Anton Tykhyy 也有理由。请参阅我的第二个示例。没有该属性,序列化将失败。
【解决方案2】:

只要Value 采用原始值、XSD 定义类型(如stringint)或T 定义中某处提到的用户定义类型(T 本身),Gree 的解决方案就可以工作,其属性的类型等。)一旦您需要反序列化与这些不同类型的值,您必须使用XmlElementAttribute 声明所有 Value 的可能类型,例如 p>

[XmlElement (typeof (string))]
[XmlElement (typeof (int))]
[XmlElement (typeof (MyType), Namespace = "http://example.com/schemas/my")]
public object Value { get ; set ; }

【讨论】:

  • 所以,您要解释的是,“对象”类型不能在“对象”类型中反序列化,而是在某处列出的“样本”类型中......
  • 是的。 XML 序列化程序必须提前知道您的所有类型才能反序列化它们。在您的情况下,当遇到 &lt;LeftBracket&gt; 节点时,XML 反序列化器可以将字符串 'None' 转换为您的枚举类型的值,因为它知道属性 LeftBracket 具有此枚举类型。对于object 类型的属性,要么必须告诉 XML 反序列化器期望什么类型,要么手动反序列化它。
  • @serhio, Anton:如果您使用 TextReader 而不是 XmlReader,则不需要所有这些。
  • 其实你有时候确实需要这个,看帖子。
  • @Anton:没错,我忽略了这个事实。只是我不喜欢在决定添加新类型时必须记住更新此属性。好吧,在任何情况下序列化一个没有类型信息的对象都有点奇怪(实际上,我很少使用普通的object 属性来处理任何事情)。此外,通过这种方式,“值”属性在生成的 XML 中“丢失”(整数被序列化为 &lt;int&gt; 元素,字符串被序列化为 &lt;string&gt; 等)——这对 OP 来说并不重要。
【解决方案3】:

好的,试试这个,但我不确定你想要实现什么。

public class PropertyFilter
{
    public string AndOr {get; set;}
    public string LeftBracket {get; set;}
    public int Property {get; set;}
    public string Operator {get; set;}
    public object Value {get; set;}
    public string RightBracket {get; set;}
}
    public void MyMethod()
    {
        using (System.IO.StreamReader reader = new System.IO.StreamReader(@"Input.xml"))
        {
            System.Xml.Serialization.XmlSerializer serializer = new XmlSerializer(typeof(PropertyFilter[]));
            PropertyFilter[] deserialized = (PropertyFilter[])serializer.Deserialize(reader);
        }
    }

我只是将您的示例 XML 放入 Input.xml 文件中。我希望这会有所帮助。

【讨论】:

  • 对不起,没注意属性... Value字段不是字符串,而是Object。问题是关于反序列化对象,而不是字符串......字符串都可以。我举了一个字符串的例子,但可以有任何对象。
  • @serhio : 我改变了属性的类型,但机制是一样的。
  • 我理解机制,但我不喜欢结果。正如您在图像中看到的那样,我有一个 XmlNode 而不是反序列化对象中的 Object。我将值序列化为一个对象,但反序列化...一个 XmlNode!不好。
  • 为了清楚起见,我在发布该问题之前使用了您描述的“机制”,以获得反序列化的值。所以不值得将其发布为答案,因为问题是如何获得适当的价值,而不是如何(反)序列化。 Merci en tout cas d'avoir essayé de m'aider.
猜你喜欢
  • 2014-12-17
  • 1970-01-01
  • 2013-04-11
  • 2021-09-30
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多