如何使用派生类进行XML序列化?

时间:2017-08-25 15:31:49

标签: c# xmlserializer

我正在转换我的工作XML序列化,以便模型类继承自抽象基类(以允许将来使用不同的串行格式)。

我的序列化按原样正常工作但是当我切换到使用从基类派生的模型时,我得到各种异常,所以我不确定如何继续。

我的班级基类是:

namespace Data
{
    public abstract class Configuration
    {
        public abstract string Schema { get; set; }

        public abstract Command[] Commands { get; set; }
    }

    public abstract class Command
    {
        public abstract string Name { get; set; }
    }
}

我的派生具体类(在派生之前工作正常的类)然后在子命名空间中:

namespace Data.Xml
{
    [Serializable()]
    [XmlType(AnonymousType = true)]
    [XmlRoot(Namespace = "", IsNullable = false)]
    public class Configuration : Data.Configuration
    {
        [XmlAttribute("noNamespaceSchemaLocation",
            Namespace = System.Xml.Schema.XmlSchema.InstanceNamespace)]
        public override string Schema { get; set; }

        [XmlArrayItem("Command", IsNullable = false)]
        public override Data.Command[] Commands { get; set; }
    }

    [Serializable()]
    public class Command : Data.Command
    {
        public override string Name { get; set; }
    }
}

我在那个子命名空间中调用序列化器,如下所示:

public override Data.Configuration DeserializeConfig(StreamReader config)
{ 
    var cs = new XmlSerializer(typeof(Configuration),
            new Type[] { typeof(Command) });
    return (Configuration)ConfigSerializer.Deserialize(ConfigStreamReader);
}

public override string SerializeConfig(Data.Configuration c, Encoding encoding)
{
    string Output = null;
    var Stream = new MemoryStream();
    using (var Writer = new XmlTextWriter(Stream, encoding))
    {
        Writer.Formatting = Formatting.Indented;
        XmlSerializerNamespaces ns = new XmlSerializerNamespaces();
        ns.Add("xsi", XmlSchema.InstanceNamespace);
        (new XmlSerializer(typeof(Configuration))).Serialize(Writer, c, ns);
        Output = encoding.GetString(Stream.ToArray());
    }
    Stream.Dispose();
    return Output;
}

生成的XML应如下所示:

<?xml version="1.0" encoding="utf-8"?>
<Configuration 
   xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
   xsi:noNamespaceSchemaLocation="SomeSchema.xsd">
  <Commands>
    <Command>
      <Name>SomeNameValue</Name>
    </Command>
  </Commands>
</Configuration>

尝试实例化序列化程序时,我看到以下异常(上面DeserializeConfig()方法中的第一行):

  

InvalidOperationException:Types&#39; Data.Command&#39;和&#39; Data.Xml.Command&#39;两者都使用名称空间&#39;&#39;中的XML类型名称&#39; Command&#39;。使用XML属性为类型指定唯一的XML名称和/或命名空间。

我不确定为什么序列化程序试图从基类创建一些东西,确定名称是相同的,这是派生和名称空间等的想法...... 如何使用属性正确标记它以使其正确/序列化?

仅供参考:我确实已经看到了几个问题,但答案似乎都足够满足于askers的要求,我无法解决如何将信息应用到这个看似简单的场景

更新:我想出了如何在实例化时将包含的类型传递给序列化程序,而不是必须注释基类,所以我已从我的问题中删除了该部分并更新了代码。这已经过时了布鲁诺的建议和我的回答(虽然建议的问题仍然不适用)。

更新:我试图通过将派生类添加到命名空间(即将[XmlElement(Namespace = "http://www.foo.com/data/xml")]添加到派生类中的每个属性)来分隔XML命名空间中的名称,但这没有区别因为序列化器似乎仍然在&#34;看到&#34;将基类和派生类放在一起,因此认为它们都在该命名空间中。

1 个答案:

答案 0 :(得分:1)

最后翻转了解大部分内容。

我退后一步,开始使用一个非常简单的非衍生示例,并按照我的需要进行操作。

这里发生了两件事。首先是冲突类型名称,然后是冲突属性名称。虽然我对这些中的每一个都有正确的意义,但是当组合在一起时构造每个选项的选项的排列量让我感到困惑。

为了防止抽象和派生类型名称在序列化时发生冲突,我需要使派生类类型匿名(这里使用XmlType属性)。

要停止属性名称冲突,我需要忽略派生类基类中的两者属性。要在不编辑基类的情况下执行此操作,我错过了一个重要的部分XmlAttributeOverrides。我在XmlSerializer.Serialize()的MSDN文档中看到了这一点,但是在解释它所涉及的内容时,其中的信息非常少。 This answer另一个问题让我想起了大卫伍德沃德excellent explanation

我还没有尝试使用派生类型列表属性或反序列化。

下面是一个程序的完整基本示例,该程序在控制台输出上输出带有一些序列化XML的字符串:

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

namespace Test
{
    class Program
    {
        static void Main(string[] args)
        {
            var TestBar = new MyXml.Bar()
            {
                Name = "John Smith",
            };

            Serializer s = new MyXml.Serializer();
            var TestOutput = s.Serialize(TestBar);
            Console.WriteLine(TestOutput);
        }
    }

    public abstract class Bar
    {
        public abstract string Name { get; set; }
    }

    public abstract class Serializer
    {
        public abstract string Serialize(Bar bar);
    }

    namespace MyXml
    {
        public class Serializer : Test.Serializer
        {
            public override string Serialize(Test.Bar bar)
            {
                string Output = null;
                var Stream = new MemoryStream();
                var Encoding = new UTF8Encoding(false, true);

                // Ignore the Name property in the *base* class!
                var ao = new XmlAttributeOverrides();
                var a = new XmlAttributes();
                a.XmlElements.Clear(); // Clear any element attributes
                a.XmlAttribute = null; // Remove any attribute attributes
                a.XmlIgnore = true; // Set the ignore attribute value true
                ao.Add(typeof(Test.Bar), "Name", a); // Set to use with Test.Bar.Name

                using (var Writer = new XmlTextWriter(Stream, Encoding))
                {
                    Writer.Formatting = Formatting.Indented;
                    var s = new XmlSerializer(typeof(Bar), ao);
                    s.Serialize(Writer, bar);
                    Output = Encoding.GetString(Stream.ToArray());
                }
                Stream.Dispose();
                return Output;
            }
        }

        [Serializable]
        [XmlType(AnonymousType = true)] // Make type anonymous!
        [XmlRoot(IsNullable = false)]
        public class Bar : Test.Bar
        {
            [XmlIgnore] // Ignore the Name property in the *derived* class!
            public override string Name
            {
                get => Unreverse(ReverseName);
                set => ReverseName = Reverse(value);
            }

            [XmlElement("Name", IsNullable = false)]
            public string ReverseName { get; set; }

            private string Unreverse(string name)
            {
                return "John Smith"; // Smith, John -> John Smith
            }

            private string Reverse(string name)
            {
                return "Smith, John"; // John Smith -> Smith, John
            }
        }
    }
}