2011-08-03 11 views
14

Czy istnieje sposób, aby spowodować XmlSerializer do serializacji prymitywne członków (np właściwości String) klasy jako atrybutów XML, nie jako elementów XML, bez konieczności pisania [XmlAttribute] z przodu każdej deklaracji własności? tj. czy istnieje przełącznik globalny, który mówi, aby XmlSerializer serializować wszystkie prymitywne elementy klasy jako atrybuty XML?Jak spowodować XmlSerializer do generowania atrybutów zamiast elementów domyślnie

Załóżmy, że mamy następujące klasy:

public class Person 
{ 
    public string FirstName 
    { 
     ... 
    } 

    public string LastName 
    { 
     ... 
    } 
} 

Następnie XmlSerializer generuje ten kod domyślnie:

<Person> 
    <FirstName>John</FirstName> 
    <LastName>Doe</LastName> 
</Person> 

Co chcę jednak jest to kod:

<Person FirstName="John" LastName="Doe"/> 

Jeszcze raz: chcę to zrobić bez [XmlAttribute] (lub bez XmlAttributeOverrides, co byłoby jeszcze bardziej pracą).

Jednym z możliwych rozwiązań byłoby użycie ogólnego etapu przetwarzania, który stosuje transformację XSLT do konwersji elementów na atrybuty. Zastanawiam się jednak, czy istnieje prostsze rozwiązanie.

+0

Odpowiedź Enrico wydaje się być rozwiązaniem tego problemu, ale z pewnością jest bardziej skomplikowana i mniej pożądana niż te, których unikasz. Być może możesz nam powiedzieć, dlaczego nie możesz użyć tych metod, abyśmy wiedzieli, co konkretnie unikasz? –

+0

Potrzebuję wymiany danych z zewnętrznym sytsem, który wymaga określonego formatu XML. W tym formacie proste typy danych są przedstawiane jako atrybuty, złożone typy danych (np. Listy) są reprezentowane jako atrybuty.Model danych ma około 50 klas i 500 atrybutów. Chcę uniknąć tego, że muszę napisać [XmlAttribute] przed każdym atrybutem. –

+0

@Kyle W W tym przypadku posiadanie wspólnej logiki serializacji XML w jednym miejscu (jak klasa bazowa) na pewno by miało sens, ponieważ utrzymuje rzeczy [DRY] (http://en.wikipedia.org/wiki/Don't_repeat_yourself). –

Odpowiedz

4

Jednym ze sposobów osiągnięcia tego celu jest realizację logiki serializacji w klasie bazowej realizującym interfejsIXmlSerializable. Klasy, które mają być serializowane do XML, musiałyby wtedy pochodzić z tej klasy bazowej, aby uzyskać funkcjonalność.

Oto przykład

public class XmlSerializableEntity : IXmlSerializable 
{ 
    public XmlSchema GetSchema() 
    { 
     // Implementation omitted for clarity 
    } 

    public void ReadXml(XmlReader reader) 
    { 
     // Implementation omitted for clarity 
    } 

    public void WriteXml(XmlWriter writer) 
    { 
     var properties = from property in this.GetType().GetProperties() 
         where property.PropertyType.IsPrimitive || 
           property.PropertyType == typeof(string) 
         select property; 

     foreach (var property in properties) 
     { 
      var name = property.Name; 
      var value = property.GetValue(this, null).ToString(); 
      writer.WriteAttributeString(name, value); 
     } 
    } 
} 

Tutaj używamy Reflection aby uzyskać listę właściwości z bieżącego obiektu, którego typ jest prymitywne lub String. Te właściwości są następnie zapisywane na wyjściu XML jako atrybuty przy użyciu dostarczonego obiektu XmlWriter.

Klasy być w odcinkach po prostu muszą dziedziczyć XmlSerializableEntity aby automatycznie uzyskać to zachowanie:

[Serializable] 
public class Foo : XmlSerializableEntity 
{ 
    public int Bar { get; set; } 
} 
+1

Wadą tego rozwiązania jest to, że inne atrybuty serializacji XML, takie jak [XmlIgnore], nie działałyby już w klasach pochodnych od XmlSerializableEntity (chyba że będę ponownie implementować całe przetwarzanie Atrybutów XmlSerializer w XmlSerializableEntity). –

+0

@fmunkert Rzeczywiście, musisz sprawdzić plik [XmlIgnoreAttribute] (http://msdn.microsoft.com/en-us/library/system.xml.serialization.xmlignoreattribute.aspx) za pomocą Reflection w 'XmlSerializableEntity' klasa. Jednak posiadanie tego rodzaju wyspecjalizowanych wspólnych zachowań w centralnej lokalizacji z pewnością przyniesie korzyści przyszłym modyfikacjom. –

+0

Akceptuję to rozwiązanie, ponieważ odpowiada na moje pytanie. Wdrożę to jednak inaczej, ponieważ rozwiązanie Enrico wymagałoby zbyt wiele pracy, jeśli nadal chcę móc korzystać ze wszystkich klas atrybutów Xml *. Zastanawiam się nad dynamicznym generowaniem instancji XmlAttributeOverrides za pomocą refleksji, która przesłania wszystkie prymitywne właściwości; lub alternatywnie, mogę napisać kod do automatycznego generowania klas proxy za pomocą CodeDom. –

0

myślę XSLT jest najbardziej stabilny, łatwy w utrzymaniu i elegancki sposób, aby przejść. Nie wymaga ponownego oparcia wszystkiego, polega na już przetestowanej serializacji, zezwala na niestandardowe atrybuty xml w klasie, i może być wykonane w pamięci za pomocą skompilowanej transformacji.

Oto generic XSLT, że powinniśmy to zrobić:

<?xml version=’1.0′ encoding=’utf-8′?> 
<xsl:stylesheet version=’1.0′ xmlns:xsl=’http://www.w3.org/1999/XSL/Transform’ xmlns:msxsl=’urn:schemas-microsoft-com:xslt’ exclude-result-prefixes=’msxsl’> 
<xsl:template match=’*'> 
<xsl:copy> 
<xsl:for-each select=’@*|*[not(* or @*)]‘> 
<xsl:attribute name=’{name(.)}’><xsl:value-of select=’.'/> 
</xsl:attribute> 
</xsl:for-each> 
<xsl:apply-templates select=’*[* or @*]|text()’/> 
</xsl:copy> 
</xsl:template> 
</xsl:stylesheet> 

Mimo, że zastanawiam się, czy elementy są wolniejsze niż atrybutami, zwłaszcza gdy wysłany całej linii.