Serializacja XML - Ukryj wartości null

130

Czy podczas używania standardowego serializatora .NET Xml można ukryć wszystkie wartości null? Poniżej znajduje się przykład wyniku mojej klasy. Nie chcę wyprowadzać liczb całkowitych dopuszczających wartość null, jeśli są ustawione na null.

Bieżące wyjście XML:

<?xml version="1.0" encoding="utf-8"?>
<myClass>
   <myNullableInt p2:nil="true" xmlns:p2="http://www.w3.org/2001/XMLSchema-instance" />
   <myOtherInt>-1</myOtherInt>
</myClass>

Czego chcę:

<?xml version="1.0" encoding="utf-8"?>
<myClass>
   <myOtherInt>-1</myOtherInt>
</myClass>
GuruMeditation
źródło

Odpowiedzi:

258

Możesz utworzyć funkcję z wzorcem, ShouldSerialize{PropertyName}który informuje XmlSerializer, czy powinien serializować element członkowski, czy nie.

Na przykład, jeśli wywoływana jest właściwość klasy, MyNullableIntmożesz mieć

public bool ShouldSerializeMyNullableInt() 
{
  return MyNullableInt.HasValue;
}

Oto pełna próbka

public class Person
{
  public string Name {get;set;}
  public int? Age {get;set;}
  public bool ShouldSerializeAge()
  {
    return Age.HasValue;
  }
}

Serializowane za pomocą następującego kodu

Person thePerson = new Person(){Name="Chris"};
XmlSerializer xs = new XmlSerializer(typeof(Person));
StringWriter sw = new StringWriter();
xs.Serialize(sw, thePerson);

Wyniki w następującym formacie XML - Zauważ, że nie ma wieku

<Person xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xmlns:xsd="http://www.w3.org/2001/XMLSchema">
  <Name>Chris</Name>
</Person>
Chris Taylor
źródło
10
Jedno słowo: super! MSDN ShouldSerialize
scheien
7
Wzorzec ShouldSerialize działa tylko wtedy, gdy właściwość nie jest oznaczona atrybutem XmlAttribute (myślałem, że to powinno działać, ponieważ atrybut może być opcjonalny, ale tak nie jest).
Matze
@Matze interesujące, nie próbowałem tego. Myślałem też, że to zadziała.
Chris Taylor,
@ChrisTaylor Yes; Założyłem to samo. Trudność polegała na tym, że utworzenie wystąpienia XmlSerializer nie powiodło się (z powodu błędu podczas odzwierciedlania typu), dopóki nie usunąłem XmlAttribute z wartości nullable int-property.
Matze
2
@PierredeLESPINAY - Od Visual Studio 2015 i nowszych możesz użyć: public bool ShouldSerializeAge () => Age.HasValue;
RooiWillie,
35

Oprócz tego, co napisał Chris Taylor: jeśli masz coś serializowanego jako atrybut, możesz mieć właściwość w swojej klasie o nazwie, {PropertyName}Specifiedaby kontrolować, czy powinna być serializowana. W kodzie:

public class MyClass
{
    [XmlAttribute]
    public int MyValue;

    [XmlIgnore]
    public bool MyValueSpecified;
}
Daniel Rose
źródło
Uważaj, {PropertyName}Specifiedatrybuty muszą być typu bool.
sinsedrix
Działa również jako funkcja. Na przykład, jeśli MyValuetak int?, można to zrobić public bool MyValueSpecified => MyValue.HasValue;.
OfirD
31

Istnieje własność zwana XmlElementAttribute.IsNullable

Jeśli właściwość IsNullable ma wartość true, atrybut xsi: nil jest generowany dla elementów członkowskich klasy, dla których ustawiono odwołanie o wartości null.

Poniższy przykład przedstawia pole z XmlElementAttributezastosowanym do niego, a właściwość IsNullable ustawiona na false.

public class MyClass
{
   [XmlElement(IsNullable = false)]
   public string Group;
}

Możesz spojrzeć na inne, XmlElementAttributeaby zmienić nazwy w serializacji itp.

JPBlanc
źródło
12
Niestety działa to tylko dla typów referencyjnych, a nie dla typów wartości lub ich odpowiedników dopuszczających wartość Nullable.
Vincent Sels
3
@VincentSels jest poprawne. MSDN mówi: Nie można zastosować właściwości IsNullable do elementu członkowskiego wpisanego jako typ wartości, ponieważ typ wartości nie może zawierać wartości null. Ponadto nie można ustawić tej właściwości na false dla typów wartości dopuszczających wartość null. Gdy takie typy mają wartość null, zostaną serializowane przez ustawienie xsi: nil na true.
bouvierr
12

Można zdefiniować pewne wartości domyślne i zapobiega to serializacji pól.

    [XmlElement, DefaultValue("")]
    string data;

    [XmlArray, DefaultValue(null)]
    List<string> data;
Michael Tak
źródło
Niestety, to nie działa w przypadku typów wartości
null
2

Wolę tworzyć własne pliki XML bez tagów generowanych automatycznie. W tym mogę zignorować tworzenie węzłów z wartościami null:

public static string ConvertToXML<T>(T objectToConvert)
    {
        XmlDocument doc = new XmlDocument();
        XmlNode root = doc.CreateNode(XmlNodeType.Element, objectToConvert.GetType().Name, string.Empty);
        doc.AppendChild(root);
        XmlNode childNode;

        PropertyDescriptorCollection properties = TypeDescriptor.GetProperties(typeof(T));
        foreach (PropertyDescriptor prop in properties)
        {
            if (prop.GetValue(objectToConvert) != null)
            {
                childNode = doc.CreateNode(XmlNodeType.Element, prop.Name, string.Empty);
                childNode.InnerText = prop.GetValue(objectToConvert).ToString();
                root.AppendChild(childNode);
            }
        }            

        return doc.OuterXml;
    }
Durga Nunna
źródło
1
private static string ToXml(Person obj)
{
  XmlSerializerNamespaces namespaces = new XmlSerializerNamespaces();
  namespaces.Add(string.Empty, string.Empty);

  string retval = null;
  if (obj != null)
  {
    StringBuilder sb = new StringBuilder();
    using (XmlWriter writer = XmlWriter.Create(sb, new XmlWriterSettings() { OmitXmlDeclaration = true }))
    {
      new XmlSerializer(obj.GetType()).Serialize(writer, obj,namespaces);
    }
    retval = sb.ToString();
  }
  return retval;
}
Rauld
źródło
1

W moim przypadku wszystkie zmienne / elementy dopuszczające wartość null były typu String. Więc po prostu sprawdziłem i przypisałem im ciąg. Puste w przypadku NULL. W ten sposób pozbyłem się niepotrzebnych atrybutów nil i xmlns (p3: nil = "true" xmlns: p3 = "http://www.w3.org/2001/XMLSchema-instance)

// Example:

myNullableStringElement = varCarryingValue ?? string.Empty

// OR

myNullableStringElement = myNullableStringElement ?? string.Empty
Sagar
źródło
1
To rozwiązanie jest bardzo ograniczone i działa tylko ze sznurkiem. W przypadku innych typów pusty ciąg nadal jest wartością. Niektóre parsery próbują znaleźć atrybut i jeśli zostaną znalezione, spróbują przekonwertować wartość na typ docelowy. W przypadku takich parserów brakujący atrybut oznacza null, a jeśli istnieje atrybut, musi mieć prawidłową wartość.
ZafarYousafi,