2009-12-22 2 views
0

Как я могу заставить XmlSerializer .NET добавить xsi: type = "FooClass" члену/узлу типа FooClass?Как я могу заставить использовать атрибут xsi: type?

Сценарий представляет собой в настоящее время выпустила приложение, где в т.1:

  • FooClass наследует FooBaseClass
  • FooPropertyA на FooBaseClass
  • FooPropertyB на FooClass
  • FooBaseClass украшен [ XmlInclude (typeof (FooClass))
  • BazClass имеет член Foo типа FooBaseClass
  • Все комплекты Baz. Foo являются к экземпляру FooClass
  • Все Обычаи Baz.Foo ожидают FooPropertyB (и поэтому экземпляр FooClass против FooBaseClass)

Цель: Удалить FooBaseClass полностью, толкая членов FooBaseClass в очередной раз в FooClass, при сохранении обратная совместимость сериализации

проблема: Тогда я теряю XSI: тип = атрибут «FooClass» на сериализации Baz.Foo.

Другими словами XmlSerializer.Serialize выход

public class BazClass 
{ 
    public BazClass() 
    { 
     Foo = new FooClass { A = 5, B = "Hello" }; 
    } 
    public FooClass Foo { get; set; } 
} 

public class FooClass 
{ 
    public int FooPropertyA { get; set; } 
    public string FooPropertyB { get; set; } 
} 

потребности быть

<Baz> 
    <Foo xsi:type="FooClass"> 
     <FooPropertyA>Hello</FooPropertyA> 
     <FooPropertyB>5</FooPropertyB> 
    </Foo> 
</Baz> 

не Удаление FooBasClass легко, но затем XmlSerializer больше не ставит XSI: тип = "FooClass" на Baz/Foo, и поэтому v.1 XmlSerializer.Deserialize создает экземпляр FooBaseClass, не устанавливая FooPropertyB, и присваивает его свойству Foo родительского экземпляра Baz. Таким образом, любой код, который проверяет, является ли Baz.Foo FooClass или выполняется напрямую, терпит неудачу.

XSI: атрибут типа был помещен автоматически в т.1 код, который был

public class BazClass 
{ 
    public BazClass() 
    { 
     Foo = new FooClass { A = 5, B = "Hello" }; 
    } 
    public FooBaseClass Foo { get; set; } 
} 

public class FooClass : FooBaseClass 
{ 
    public string FooPropertyB { get; set; } 
} 

[XmlInclude(typeof(FooClass))]  
public class FooBaseClass 
{ 
    public int FooPropertyA { get; set; } 
} 

Я думаю, короткий ответ, что вы не можете - по крайней мере, без реализации I (Xml) Сериализуемый или написание пользовательского кода сериализации. Тем не менее, я открыт для хороших предложений. Между тем, я применил обходное решение, и я надеюсь на что-то более элегантное, или это по крайней мере позволяет мне как-то полностью удалить FooBaseClass.

BazClass 
{ 
    [XmlElement("Foo")] 
    public FooBaseClass XmlFoo { get { return Foo; } set { Foo = (StartPicture)value; } } 

    [XmlIgnore] 
    public FooClass Foo { get; set; } 
}  

FooClass : FooBaseClass 
{ 
    public int FooPropertyB { get; set; } 
    public string FooPropertyA { get; set; } 
} 

[XmlInclude("FooClass")] 
FooBaseClass 
{ 
} 

ответ

3

XmlSerializer может быть довольно немым и понятным в разы, что в свою очередь работает в ваших интересах. Просто поместите его туда вручную:

public class FooClass 
{ 
    public int FooPropertyA { get; set; } 
    public string FooPropertyB { get; set; } 

    [XmlAttribute("type", Namespace="http://www.w3.org/2001/XMLSchema-instance")] 
    public string XsiType 
    { 
     get { return "Foo"; } 
     set { } 
    } 
} 
+0

работал как шарм! значение второго набора глаз ... :-) –

+0

Не работает, когда тип находится в другом пространстве имен. –

+0

Пространство имен в этом объявлении предназначено для атрибута 'type', а не для самого типа. Если вам нужен тип, который находится в пространстве имен, отличном от того самого элемента, то я думаю, что вы можете взломать его аналогичным образом, вручную введя атрибут 'xmlns' (используя свойство фиктивного' [XmlAttribute] ') , а затем ссылаясь на это в имени типа. –

1

У меня не было никаких проблем продуцирующих следующего:

<?xml version="1.0" encoding="utf-8"?> 
<BazClass xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
    xmlns:xsd="http://www.w3.org/2001/XMLSchema"> 
    <Foo xsi:type="FooClass"> 
     <FooPropertyA>Hello</FooPropertyA> 
     <FooPropertyB>5</FooPropertyB> 
    </Foo> 
</BazClass> 

из

[XmlInclude(typeof(FooClass))] 
//[XmlType(TypeName = "FooBase", Namespace = "urn:namespace", IncludeInSchema = true)] 
public class FooBaseClass 
{ 
    public string FooPropertyA { get; set; } 
} 

//[XmlType(TypeName = "Foo", Namespace = "urn:namespace", IncludeInSchema = true)] 
public class FooClass : FooBaseClass 
{ 
    public int FooPropertyB { get; set; } 
} 

public class BazClass 
{ 
    public FooBaseClass Foo { get; set; } 
} 

(обратите внимание на XmlType атрибутов закомментированы.Я хотел посмотреть, что произошло, если было указано пространство имен)

Пожалуйста, покажите код, который вы использовали, и полученный XML-код.

+0

Я (надеюсь) разъяснил мой вопрос выше. Да, ваш пример будет работать, но он не делает то, что я хочу - то есть удалить FooBaseClass, нажав FooPropertyB на FooClass. Ваш пример фактически отражает мое текущее состояние - не мое желаемое состояние. –

+0

Я только что видел, как моя первая строка вопроса действительно была довольно обманчивой - мои извинения. –

0

Для поддержки наследования типов в других пространствах имен вы должны использовать решение, похожее на то, что предложил Павел Минаев, но с XmlQualifiedName набрали свойство вместо строки, например

using System.Xml; 
using System.Xml.Schema; 
using System.Xml.Serialization; 

namespace Test 
{ 
    /// <summary> 
    /// Base class which is XML serializable and extensible. 
    /// </summary> 
    [XmlRoot(XmlRootName, Namespace = XmlRootNamespace)] 
    public abstract class BaseClassInOtherNamespace 
    { 
     /// <summary> 
     /// Name of the XML element 
     /// </summary> 
     public const string XmlRootName = "Base"; 

     /// <summary> 
     /// XML namespace in which this type is defined. 
     /// </summary> 
     public const string XmlRootNamespace = "urn:base"; 

     /// <summary> 
     /// Creates an instance which serializes as the correct inherited XML type. 
     /// </summary> 
     protected BaseClassInOtherNamespace(XmlQualifiedName xsiType) 
     { 
      XsiType = xsiType; 
     } 

     /// <summary> 
     /// XML type for serialization. 
     /// </summary> 
     [XmlAttribute("type", Namespace = XmlSchema.InstanceNamespace)] 
     public XmlQualifiedName XsiType { get; set; } 

     /// <summary> 
     /// Some base property. 
     /// </summary> 
     public int BaseProperty { get; set; } 
    } 

    /// <summary> 
    /// Inheriting class extending the base class, created in a different XML namespace. 
    /// </summary> 
    [XmlRoot(XmlRootName, Namespace = XmlRootNamespace)] 
    [XmlType(XmlTypeName, Namespace = XmlTypeNamespace)] 
    public class InheritingClass : BaseClassInOtherNamespace 
    { 
     /// <summary> 
     /// Name of the XML element 
     /// </summary> 
     public const string XmlTypeName = "Inheriting"; 

     /// <summary> 
     /// XML namespace in which this type is defined. 
     /// </summary> 
     public const string XmlTypeNamespace = "urn:other"; 

     /// <summary> 
     /// Creates an instance. 
     /// </summary> 
     public InheritingClass() : base(new XmlQualifiedName(XmlTypeName, XmlTypeNamespace)) 
     { 
     } 

     /// <summary> 
     /// Some new property in a different (inheriting) namespace. 
     /// </summary> 
     public int InheritingProperty { get; set; } 
    } 
} 

будет сериализовать (и де-сериализации) правильно, как:

<Base xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:q1="urn:other" xsi:type="q1:Inheriting" xmlns="urn:base"> 
    <BaseProperty>0</BaseProperty> 
    <q1:InheritingProperty>0</q1:InheritingProperty> 
</Base> 

Это удовлетворяет требованию по-настоящему расширяемый полиморфных типов XML, то есть базовый класс может быть использован в любом месте, а затем дополнения могут быть назначенным, сериализованным и де-сериализованным правильно как в .NET, так и с проверкой XSD.

Принимая его дальше, вы можете также добавить свойство XmlSerializerNamespaces с XmlNamespaceDeclarationsAttribute указать предпочтительные префиксы и удалить любые нежелательные пространства имен, таких как Xmlns: XSD (мы используем только Xmlns: XSI) или даже пространство имен XSI для вашего не -Удерживание XML-сериализованных классов.

Смежные вопросы