【问题标题】:Serialize Base Class Implementation for all "new" Properties序列化所有“新”属性的基类实现
【发布时间】:2015-09-22 11:27:05
【问题描述】:

我有一堆类是从 XSD 自动生成的,并在我没有源代码的已编译 DLL 中提供给我。我需要为每种类型添加接口,导致代码如下:

public interface IBar
{
    string SomeProperty { get; set; }
}

public interface IFoo<TBar> where TBar : IBar
{
    TBar Bar { get; set; }
}

public class BarWrapper : BarFromXSD, IBar
{
}

public class FooWrapper : FooFromXSD, IFoo<BarWrapper>
{
    [XmlElement("bar")]
    public new BarWrapper Bar
    {
        get { return base.Bar as BarWrapper; }
        set { base.Bar = value; }
    }
}

如果客户端给我一个 DLL,其中任何底层类型的接口发生变化,我会收到编译时错误告诉我这样的情况。但是,如果底层 DLL 中的序列化属性发生更改,则情况并非如此。在这种情况下,我的包装类将愉快地序列化为与相关 XSD 不兼容的对象。

上述代码的另一个问题是它根本不起作用。当我尝试为FooWrapper 类型的对象创建XmlSerializer 时,我得到了异常链:

反映类型“MyNamespace.FooWrapper”时出错。
反映属性“Bar”时出错。
MyNamespace.BarWrapper 类型的成员 FooWrapper.Bar 隐藏了 DLLNamespace.Bar 类型的基类成员 cs_Foo.Bar。使用 XmlElementAttribute 或 XmlAttributeAttribute 指定新名称。

为了避免这个问题,我想做一个更简单的:

1) 覆盖默认序列化,以忽略“新”属性实现,或者
2)反射性地将所有XML序列化属性从基类复制到派生类

我试图通过任何潜在解决方案解决的问题是:

1) 我想在静态构造函数中执行一次反射,以确定序列化的元素/属性名称和命名空间。
2) 我有多个类遵循与FooWrapper 相同的模式,因此任何解决方案都应该适用于任何此类。
3) 遵循FooWrapper 模式的类可以包含其他未在需要序列化的基类中定义的属性。
4) 理想的解决方案应该优雅地处理新属性。例如,如果稍后我添加或删除“新”属性,我不应该添加/删除其他方法,或者必须在静态构造函数中硬编码“新”属性的名称。

非常感谢您对满足这些要求的解决方案的任何见解。

【问题讨论】:

  • 使用 XML 序列化继承的类向 xml 添加一个属性以区分子类。如果没有此属性,您将无法使用序列化读取 xml。所以它也必须添加到dll中。您不必只编写自己的代码来读取和解析 xml,而不是使用标准的 xml 序列化。
  • 我从不需要反序列化任何FooWrapper 模式类型。我希望它们像序列化类型 Foo 一样被序列化,但具有潜在的附加属性(例如,所有元素都可以具有 xsi:type 属性)。至于编写我自己的代码,这很好,但它必须默认使用XmlSerializer,因为我无法修改的 DLL 具有基于传递给方法。因此,任何解决方案都必须与new XmlSerializer(typeof(FooWrapper)) 构造函数兼容。
  • De-Serializer 将使用哪些方法。如果您正在修改序列化器,真正关心的是稍后将反序列化代码的代码。如果您还可以更改反序列化,则更改序列化程序没有问题。
  • 我根本没有反序列化结果。我只是从对象生成 XML,然后将该 XML 发送到另一台服务器进行处理。我想修改类来实现接口,因为同一个 XSD 的多个版本具有相似的结构。如果每个类都实现一个通用接口,我可以跨多个版本执行通用逻辑。但是,我无权访问生成的类的源代码。因此,我需要某种方法来使用从我无法修改的类继承的自定义类来生成相同的 XML。有没有办法做到这一点?
  • 我不知道服务器期望什么,所以我无法回答这个问题。

标签: c# serialization reflection xml-serialization


【解决方案1】:

这是一个非常简单的带有继承的xml示例。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml;
using System.Xml.Serialization;
using System.IO;

namespace ConsoleApplication51
{
    class Program
    {
        const string FILENAME = @"c:\temp\test.xml";
        static void Main(string[] args)
        {
            Root root = new Root() {
                bar = new Bar1() {
                    foo = "123"
                }
            };


            XmlSerializer serializer = new XmlSerializer(typeof(Root));

            StreamWriter writer = new StreamWriter(FILENAME);
            XmlSerializerNamespaces _ns = new XmlSerializerNamespaces();
            _ns.Add("", "");
            serializer.Serialize(writer, root, _ns);
            writer.Flush();
            writer.Close();
            writer.Dispose();

        }
    }

    [XmlRoot("Root")]
    public class Root 
    {
        public Bar bar {get;set;}
    }

    [XmlInclude(typeof(Bar1))]
    [XmlRoot("Bar")]
    public class Bar 
    {
    }

    [XmlRoot("Bar1")]
    public class Bar1 : Bar
    {
        [XmlElement("foo")]
        public string foo {get;set;}
    }
}

查看 XML。添加了“类型”属性。

<?xml version="1.0" encoding="utf-8"?>
<Root>
  <bar d2p1:type="Bar1" xmlns:d2p1="http://www.w3.org/2001/XMLSchema-instance">
    <foo>123</foo>
  </bar>
</Root>

【讨论】:

  • 您的示例存在一些问题。首先,我不控制序列化代码。现有代码(我无法修改)是var serializer = new XmlSerializer(xmlObj.GetType()); serializer.Serialize(xmlObj); 鉴于此约束,有没有办法摆脱“bar”上的 d2p1:type 属性(没有继承就不会存在)?此外,属性foo 需要添加到类Bar,并在Bar1 中覆盖(使用“new”关键字)。我的问题是,考虑到这些约束,是否可以生成您生成的 XML,减去 d2pi:type 属性?
  • 您无法使用序列化来摆脱 type 属性。这就是我一直在说的。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-09-14
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2010-11-18
相关资源
最近更新 更多