Проблема приведения наследования с помощью Generics и XmlSerializer - PullRequest
1 голос
/ 06 августа 2009

Как я могу привести экземпляр объекта и фактически сделать его объектом этого типа?

У меня есть класс myClass1, который является базовым классом для myClass2 и myClass3. Я хочу использовать myClass1 для аудита, для аудита все, что мне нужно, это данные из myClass1. Поскольку myClass2 и myClass3 наследуются от myClass1, вы можете установить экземпляр myClass1 для экземпляра myClass2, например:

myClass2 foo = new myClass2();
foo.prop1 = "some data";
foo.prop2 = "some More Data";

myClass1 bar = foo;

проблемы возникают, потому что я использую универсальный

 public static IXPathNavigable SerializeGeneric<T>(T serializableObject)
    {
        String XmlizedString = "Error processing request";
        XmlDocument XMLObject = new XmlDocument();
        try
        {
            MemoryStream memoryStream = new MemoryStream();
            XmlSerializer xs = new XmlSerializer(serializableObject.GetType());

для передачи класса, когда я сериализирую его и XmlSerializer выдает ошибку, потому что даже если я преобразовал его как myClass1, базовый объект все еще является myClass2, вы можете увидеть это, приведя его к объекту, а затем проверив тип и XmlSerializer get's сбит с толку, потому что я говорю это, чтобы сделать его class1 быть, хотя это его собственное отражение, он видит это как myClass2

myClass2 foo = new myClass2();
foo.prop1 = "some data";
foo.prop2 = "some More Data";

myClass1 bar = foo;
object obj = bar;
string name = obj.GetType().Name;

значение name - "myClass2", что имеет смысл видеть, что данные в памяти на самом деле являются myClass2, под строкой просто указатель на объект myClass2. Без создания нового экземпляра и установки значений этого нового экземпляра для этого объекта, например

myClass1 bar = new myClass1(){prop1=foo.prop1, prop2=foo.prop2};

Я действительно не хочу делать это таким образом.

Ответы [ 2 ]

1 голос
/ 06 августа 2009

Не знаю, сработает ли это, но попробуйте изменить его на:

XmlSerializer xs = new XmlSerializer(typeof(T));

это скажет сериализатору создать экземпляр сериализатора любого типа, который вы укажете. Хотя я не уверен, что сериализатор это сделает.

Изменить: При условии, что вы звоните

SerializeGeneric<MyClass1>(foo);

Редактировать снова:

Только что попробовал с этим:

    public void Test()
    {
        var obj = new Foo2() { Prop1 = "Test", Prop2 = "Test2" };

        SerializeGeneric((Foo1)obj);
    }

    private void SerializeGeneric<T>(T obj)
    {
        StringWriter writer = new StringWriter();    
        XmlSerializer xs = new XmlSerializer(typeof(T));
        xs.Serialize(writer, obj);

        Console.WriteLine(writer.ToString());
    }


    public class Foo1
    {
        public string Prop1 { get; set; }
    }

    public class Foo2 : Foo1
    {
        public string Prop2 { get; set; }
    }

Выдает исключение «Неожиданный тип». Оказывается, сериализатор не будет сериализовать объект как другой тип. Не уверен, что это можно сделать.

Полагаю, вы могли бы написать собственный сериализатор или написать простой метод отражения, который выполняет операцию memberwiseclone-ish, которая копирует только те свойства из foo1, которые вы хотите.

Интересно, что если вы добавите [XmlInclude (typeof (Foo2))] к объявлению Foo1, то это не будет ошибкой, хотя она выдаст этот бред:

<Foo1 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xsi:type="Foo2">
  <Prop1>Test</Prop1>
  <Prop2>Test2</Prop2>
</Foo1>

Это объявление Foo1 со свойствами Foo1 и Foo2, с типом, объявленным как Foo2 ... интересно.

Последний:

Это работает, хотя я не уверен, что рекомендую.

    public void Test ()
    {
        var obj = new Foo2() { Prop1 = "Test", Prop2 = "Test2" };

        SerializeGeneric(ShallowCopy<Foo1>(obj));
    }

    private T ShallowCopy<T>(object input)
        where T : class, new()
    {
        T newObj = Activator.CreateInstance<T>();
        Type oldType = input.GetType();
        BindingFlags flags = BindingFlags.Instance | BindingFlags.Public | BindingFlags.GetField | BindingFlags.SetField;
        var oldProperties = oldType.GetProperties(flags);

        foreach (var pd in typeof(T).GetProperties(flags))
        {
            var oldPd = oldProperties.FirstOrDefault(x=>x.Name == pd.Name && x.PropertyType == pd.PropertyType);

            if(oldPd != null)
               pd.SetValue(newObj, oldPd.GetValue(input, null), null);
        }

        return newObj;
    }

Это дает вам:

<Foo1 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
  <Prop1>Test</Prop1>
</Foo1>

, который выглядит идеально.

0 голосов
/ 06 августа 2009

Чтобы правильно выполнить сериализацию XML при наличии наследования, есть несколько рекомендаций:

  1. Всегда создавать XmlSerializer с typeof(Base) (при сериализации или десериализации)
  2. Установить атрибут XmlInclude(typeof(Derived)) в Базовом классе (для каждого производного класса)

Это позволяет вам вызывать десериализацию любого xml, который представляет любой объект в иерархии наследования. Есть альтернативы (например, проходя во всех возможных производных типов конструктору XmlSerializer, но спорно, если это улучшение по сравнению с атрибутом)

...