Serialización Xml - Ocultar valores nulos

128

Cuando uso un serializador .NET Xml estándar, ¿hay alguna forma de ocultar todos los valores nulos? El siguiente es un ejemplo de la salida de mi clase. No quiero generar los enteros anulables si están configurados como nulos.

Salida actual de 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>

Lo que quiero:

<?xml version="1.0" encoding="utf-8"?>
<myClass>
   <myOtherInt>-1</myOtherInt>
</myClass>
Meditación Gurú
fuente

Respuestas:

255

Puede crear una función con el patrón ShouldSerialize{PropertyName}que le dice al XmlSerializer si debe serializar al miembro o no.

Por ejemplo, si se llama a su propiedad de clase MyNullableInt, podría tener

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

Aquí hay una muestra completa

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

Serializado con el siguiente código

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

Resultados en el siguiente XML: observe que no hay edad

<Person xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xmlns:xsd="http://www.w3.org/2001/XMLSchema">
  <Name>Chris</Name>
</Person>
Chris Taylor
fuente
9
Una palabra: impresionante! MSDN
Debería Serializar
77
El patrón ShouldSerialize solo funciona si la propiedad no está marcada con un atributo XmlAttribute (pensé que debería funcionar, porque un atributo podría ser opcional, pero no lo hace).
Matze
@ Matze interesante, no he intentado eso. También habría asumido que funcionaría.
Chris Taylor
@ChrisTaylor Sí; Asumí lo mismo. Lo complicado fue que la creación de la instancia XmlSerializer falló (debido a un error al reflejar el tipo) hasta que eliminé el XmlAttribute de la propiedad int que acepta valores NULL.
Matze
2
@PierredeLESPINAY - Desde Visual Studio 2015 en adelante, puede usar: public bool ShouldSerializeAge () => Age.HasValue;
RooiWillie
34

Además de lo que escribió Chris Taylor: si tiene algo serializado como atributo, puede tener una propiedad en su clase nombrada {PropertyName}Specifiedpara controlar si debe ser serializada. En codigo:

public class MyClass
{
    [XmlAttribute]
    public int MyValue;

    [XmlIgnore]
    public bool MyValueSpecified;
}
Daniel Rose
fuente
Tenga cuidado, los {PropertyName}Specifiedatributos tienen que ser de tipo bool.
sinsedrix
30

Existe una propiedad llamada XmlElementAttribute.IsNullable

Si la propiedad IsNullable se establece en true, el atributo xsi: nil se genera para los miembros de la clase que se han establecido en una referencia nula.

El siguiente ejemplo muestra un campo con el XmlElementAttributeaplicado y la propiedad IsNullable establecida en falso.

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

Puede echar un vistazo a otros XmlElementAttributepara cambiar los nombres en la serialización, etc.

JPBlanc
fuente
11
Desafortunadamente, esto solo funciona para los tipos de referencia, no para los tipos de valor o sus contrapartes anulables.
Vincent Sels
3
@VincentSels es correcto. MSDN dice: No puede aplicar la propiedad IsNullable a un miembro escrito como un tipo de valor porque un tipo de valor no puede contener nulo. Además, no puede establecer esta propiedad en falso para los tipos de valores anulables. Cuando tales tipos son nulos, se serializarán configurando xsi: nil en verdadero.
bouvierr
12

Puede definir algunos valores predeterminados y evita que los campos se serialicen.

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

    [XmlArray, DefaultValue(null)]
    List<string> data;
MichaelSo
fuente
Desafortunadamente, esto no funciona para los tipos de valores anulables
bubi
2

Prefiero crear mi propio xml sin etiquetas autogeneradas. En esto puedo ignorar la creación de nodos con valores nulos:

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
fuente
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
fuente
1

En mi caso, las variables / elementos anulables eran todos de tipo String. Entonces, simplemente realicé una verificación y les asigné una cadena. Vacío en caso de NULL. De esta manera me libré de los atributos innecesarios nil y xmlns (p3: nil = "true" xmlns: p3 = "http://www.w3.org/2001/XMLSchema-instance)

// Example:

myNullableStringElement = varCarryingValue ?? string.Empty

// OR

myNullableStringElement = myNullableStringElement ?? string.Empty
Sagar
fuente
1
Esta solución es muy limitada y solo funciona con string. Para otros tipos, la cadena vacía sigue siendo un valor. Algunos analizadores intentan encontrar el atributo y, si se encuentra, intenta convertir el valor al tipo de destino. Para tales analizadores, el atributo faltante significa nulo y, si hay un atributo, debe tener un valor válido.
ZafarYousafi