2009-10-07 6 views
27

ich eine Klasse wie diese sind der Typen allerWie null Eigenschaften auszuschließen, wenn XmlSerializer mit

public MyClass 
{ 
    public int? a { get; set; } 
    public int? b { get; set; } 
    public int? c { get; set; } 
} 

Serialisierung nullable, weil ich minimale Daten wollen gespeichert, wenn ein Objekt dieser Art Serialisierung. Wenn jedoch ist es mit nur „a“ bevölkert serialisiert, erhalte ich die folgende xml

<MyClass ...> 
    <a>3</a> 
    <b xsi:nil="true" /> 
    <c xsi:nil="true" /> 
</MyClass> 

Wie kann ich dies nur xml einrichten für die nicht null Eigenschaften erhalten? Der gewünschte Ausgang würde

sein
<MyClass ...> 
    <a>3</a> 
</MyClass> 

ich diese Nullwerte ausschließen will, weil es mehrere Eigenschaften sein wird, und dies wird in einer Datenbank gespeichert zu werden (ja, das ist nicht mein Anruf), so mag ich die nicht benötigten Daten halten minimal .

+3

Wenn Sie die ganze Zeit aufzählen, die Entwickler vergeuden, um zu versuchen, xml zu sehen, wie sie * denken *, sollte es aussehen ... Sie hätten einen ganzen Haufen Entwicklerstunden. Ich habe vor langer Zeit aufgegeben. Sie sollten das als eine Option betrachten. – Will

+1

@Will würde ich normalerweise, überhaupt kein Problem, aber das wird tausendmal am Tag verwendet werden und die ganze Klasse, serialisiert, ist etwa 1000 Zeichen, das ist, wenn alle Eigenschaften Null sind! Auch das alles geht in der db, nicht meine Wahl :( –

+3

Dies ist eine gute Frage, aber ich denke, es ist ein Duplikat von http://StackOverflow.com/Questions/1296468/Suppress-Null-Value-Types-from (was Marc Gravell antwortete, indem er das Spezifikationsmuster diskutierte) –

Antwort

6

Ich nehme an, Sie könnten einen XmlWriter erstellen, die alle Elemente filtert mit einem xsi: nil-Attribut, und übergibt alle anderen Anrufe an den zugrundeliegenden wahren Schriftsteller.

+0

Ich mag es, sehr einfach, tolle Idee :) –

+1

Ich postete meine Implementierung von diesem (und auch die Möglichkeit, Namespaces überspringen) unter https://plus.google.com/+JoelElliott27/posts/ePPuwnH5Za8 – Abacus

+0

@Abacus danke für Ihren Beitrag, aber es entfernt nicht das komplette Element. Oder fehlt mir etwas? – ArieKanarie

28

Sie ignorieren bestimmte Elemente mit specification

public MyClass 
{ 
    public int? a { get; set; } 

    [System.Xml.Serialization.XmlIgnore] 
    public bool aSpecified { get { return this.a != null; } } 

    public int? b { get; set; } 
    [System.Xml.Serialization.XmlIgnore] 
    public bool bSpecified { get { return this.b != null; } } 

    public int? c { get; set; } 
    [System.Xml.Serialization.XmlIgnore] 
    public bool cSpecified { get { return this.c != null; } } 
} 

Der {Feld} Spezifische Eigenschaften wird die Serializer sagen, ob es die entsprechenden Felder oder nicht durch Rückgabe true/false serialisiert sollte.

+2

hinweis: Ich antwortete darauf, weil ich ehrlich eine bessere Lösung suche, ich bin kein großer Fan von all diesen zusätzlichen Feldern, wie meine Klasse hat mehrere Felder zu serialisieren –

+2

Ich habe vielleicht Ihren 'Bonus' missverstanden, aber null Strings automatisch weggelassen, ohne die xsi: nil = "true" Treibgut. –

+0

@ Jeff, Ah, so sie sind: - P Danke –

0

Die einfachste Art und Weise des Schreibens Code wie dieser, wo die genaue Ausgangs wichtig ist:

  1. ein XML-Schema Schreiben Sie genaue gewünschte Format zu beschreiben.
  2. Konvertieren Sie Ihr Schema in eine Klasse mit xsd.exe.
  3. Konvertieren Sie Ihre Klasse wieder in ein Schema (unter Verwendung von xsd.exe erneut) und überprüfen Sie es mit Ihrem ursprünglichen Schema, um sicherzustellen, dass der Serializer jedes gewünschte Verhalten korrekt reproduziert.

Tweak und wiederholen Sie, bis Sie Code gearbeitet haben.

Wenn Sie nicht sicher sind, welche Datentypen zuerst verwendet werden sollen, beginnen Sie mit Schritt 3 anstelle von Schritt 1 und optimieren Sie dann.

IIRC, für Ihr Beispiel werden Sie fast sicher mit Specified Eigenschaften enden, wie Sie bereits beschrieben haben, aber sie für Sie erzeugt sicher schlägt, sie von Hand zu schreiben. :-)

-2

Markieren Sie das Element mit [XmlElement ("ElementName", IsNullable = false)] Nullwerte werden weggelassen.

+1

Dies wird nicht funktionieren. Der 'XmlSerializer' löst eine Exception aus, wenn eine Eigenschaft vom Typ' int? 'Wie folgt dargestellt wird. –

3

Besser spät als nie ...

fand ich einen Weg (vielleicht nur mit dem neuesten Rahmen, weiß ich nicht), dies zu tun. I wurde mit Datamember-Attribute für einen WCF-Webservice Vertrag und ich markierte mein Objekt wie folgt:

[DataMember(EmitDefaultValue = false)] 
public decimal? RentPrice { get; set; } 
+0

DataMember befindet sich im Namespace System.Runtime.Serialization, während der SVC-Serializer System.Xml.Serialization verwendet. Dies funktioniert nicht, wenn Sie keinen anderen Serializer verwenden. –

+0

Ja, das funktioniert nur mit dem DataContractSerializer, nicht mit dem XmlSerializer – crush

2

Und noch eine Lösung: die Rettung regex verwenden \s+<\w+ xsi:nil="true" \/> alle null Eigenschaften aus einer Zeichenfolge entfernen XML enthalten. Ich stimme zu, nicht die eleganteste Lösung, und funktioniert nur, wenn Sie nur serialisieren müssen. Aber das war alles, was ich heute brauche, und ich wollte nicht {Foo}Specified Eigenschaften für alle Eigenschaften hinzufügen, die Nullable sind.

public string ToXml() 
{ 
    string result; 

    var serializer = new XmlSerializer(this.GetType()); 

    using (var writer = new StringWriter()) 
    { 
     serializer.Serialize(writer, this); 
     result = writer.ToString(); 
    } 

    serializer = null; 

    // Replace all nullable fields, other solution would be to use add PropSpecified property for all properties that are not strings 
    result = Regex.Replace(result, "\\s+<\\w+ xsi:nil=\"true\" \\/>", string.Empty); 

    return result; 
} 
2

1) Verlängerungs

public static string Serialize<T>(this T value) { 
     if (value == null) { 
      return string.Empty; 
     } 
     try { 
      var xmlserializer = new XmlSerializer(typeof(T)); 
      var stringWriter = new Utf8StringWriter(); 
      using (var writer = XmlWriter.Create(stringWriter)) { 
       xmlserializer.Serialize(writer, value); 
       return stringWriter.ToString(); 
      } 
     } catch (Exception ex) { 
      throw new Exception("An error occurred", ex); 
     } 
    } 

1a) Utf8StringWriter

public class Utf8StringWriter : StringWriter { 
    public override Encoding Encoding { get { return Encoding.UTF8; } } 
} 

2) Erstellen XElement

XElement xml = XElement.Parse(objectToSerialization.Serialize()); 

3) Entfernen des Nil

012 Speicher
xml.Descendants().Where(x => x.Value.IsNullOrEmpty() && x.Attributes().Where(y => y.Name.LocalName == "nil" && y.Value == "true").Count() > 0).Remove(); 

4) vor

xml.Save(xmlFilePath); 
2

Diese Frage ist aufgefordert worden, eine recht lange Zeit in Datei aber immer noch sehr relevant ist auch im Jahr 2017 keiner der vorgeschlagenen Antworten hier, um mich nicht zufrieden stellend waren hier so ein einfache Lösung, die ich kam:

Verwenden von regulären Ausdrücken ist der Schlüssel. Da wir nicht viel Kontrolle über das Verhalten des XmlSerializers haben, versuchen wir NICHT zu verhindern, dass es diese Nullwerttypen serialisiert. Nehmen Sie stattdessen einfach die serialisierte Ausgabe und ersetzen Sie die unerwünschten Elemente durch eine leere Zeichenfolge mit Regex. Das Muster (in C#) verwendet wird, ist:

<\w+\s+\w+:nil="true"(\s+xmlns:\w+="http://www.w3.org/2001/XMLSchema-instance")?\s*/> 

Hier ist ein Beispiel:

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

namespace MyNamespace 
{ 
    /// <summary> 
    /// Provides extension methods for XML-related operations. 
    /// </summary> 
    public static class XmlSerializerExtension 
    { 
     /// <summary> 
     /// Serializes the specified object and returns the XML document as a string. 
     /// </summary> 
     /// <param name="obj">The object to serialize.</param> 
     /// <param name="namespaces">The <see cref="XmlSerializerNamespaces"/> referenced by the object.</param> 
     /// <returns>An XML string that represents the serialized object.</returns> 
     public static string Serialize(this object obj, XmlSerializerNamespaces namespaces = null) 
     { 
      var xser = new XmlSerializer(obj.GetType()); 
      var sb = new StringBuilder(); 

      using (var sw = new StringWriter(sb)) 
      { 
       using (var xtw = new XmlTextWriter(sw)) 
       { 
        if (namespaces == null) 
         xser.Serialize(xtw, obj); 
        else 
         xser.Serialize(xtw, obj, namespaces); 
       } 
      } 

      return sb.ToString().StripNullableEmptyXmlElements(); 
     } 

     /// <summary> 
     /// Removes all empty XML elements that are marked with the nil="true" attribute. 
     /// </summary> 
     /// <param name="input">The input for which to replace the content. </param> 
     /// <param name="compactOutput">true to make the output more compact, if indentation was used; otherwise, false.</param> 
     /// <returns>A cleansed string.</returns> 
     public static string StripNullableEmptyXmlElements(this string input, bool compactOutput = false) 
     { 
      const RegexOptions OPTIONS = 
      RegexOptions.Compiled | RegexOptions.IgnoreCase | RegexOptions.IgnorePatternWhitespace | RegexOptions.Multiline; 

      var result = Regex.Replace(
       input, 
       @"<\w+\s+\w+:nil=""true""(\s+xmlns:\w+=""http://www.w3.org/2001/XMLSchema-instance"")?\s*/>", 
       string.Empty, 
       OPTIONS 
      ); 

      if (compactOutput) 
      { 
       var sb = new StringBuilder(); 

       using (var sr = new StringReader(result)) 
       { 
        string ln; 

        while ((ln = sr.ReadLine()) != null) 
        { 
         if (!string.IsNullOrWhiteSpace(ln)) 
         { 
          sb.AppendLine(ln); 
         } 
        } 
       } 

       result = sb.ToString(); 
      } 

      return result; 
     } 
    } 
} 

Ich hoffe, das hilft.