Serialização XML - Ocultar valores nulos

128

Ao usar um serializador .NET Xml padrão, existe alguma maneira de ocultar todos os valores nulos? O abaixo é um exemplo da saída da minha classe. Não quero gerar números inteiros anuláveis ​​se estiverem definidos como nulos.

Saída atual 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>

O que eu quero:

<?xml version="1.0" encoding="utf-8"?>
<myClass>
   <myOtherInt>-1</myOtherInt>
</myClass>
Meditação de guru
fonte

Respostas:

255

Você pode criar uma função com o padrão ShouldSerialize{PropertyName}que informa ao XmlSerializer se ele deve serializar o membro ou não.

Por exemplo, se sua propriedade de classe for chamada, MyNullableIntvocê poderá ter

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

Aqui está uma amostra completa

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

Serializado com o seguinte código

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

Resultados no XML a seguir - Observe que não há idade

<Person xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xmlns:xsd="http://www.w3.org/2001/XMLSchema">
  <Name>Chris</Name>
</Person>
Chris Taylor
fonte
9
Uma palavra: impressionante! MSDN ShouldSerialize
esquema
7
O padrão ShouldSerialize funciona apenas se a propriedade não estiver marcada com um atributo XmlAttribute (achei que deveria funcionar, porque um atributo poderia ser opcional, mas não).
Matze
@ Matze interessante, eu não tentei isso. Eu também teria assumido que funcionaria.
Chris Taylor
@ChrisTaylor Sim; Eu assumi o mesmo. O mais complicado foi que a criação da instância XmlSerializer falhou (devido a um erro ao refletir o tipo) até eu remover o XmlAttribute da propriedade int anulável.
Matze
2
@PierredeLESPINAY - A partir do visual studio 2015, você pode usar: public bool ShouldSerializeAge () => Age.HasValue;
RooiWillie
34

Além do que Chris Taylor escreveu: se você tiver algo serializado como um atributo, poderá ter uma propriedade em sua classe nomeada {PropertyName}Specifiedpara controlar se deve ser serializada. Em código:

public class MyClass
{
    [XmlAttribute]
    public int MyValue;

    [XmlIgnore]
    public bool MyValueSpecified;
}
Daniel Rose
fonte
Cuidado, os {PropertyName}Specifiedatributos devem ser do tipo bool.
sinsedrix
30

Existe uma propriedade chamada XmlElementAttribute.IsNullable

Se a propriedade IsNullable estiver configurada como true, o atributo xsi: nil será gerado para os membros da classe que foram configurados para uma referência nula.

O exemplo a seguir mostra um campo com o XmlElementAttributeaplicado a ele e a propriedade IsNullable definida como false.

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

Você pode procurar outro XmlElementAttributepara alterar nomes na serialização etc.

JPBlanc
fonte
11
Infelizmente, isso funciona apenas para tipos de referência, não para tipos de valor ou seus equivalentes anuláveis.
Vincent Sels
3
@VincentSels está correto. MSDN diz: Você não pode aplicar a propriedade IsNullable a um membro digitado como um tipo de valor porque um tipo de valor não pode conter nulo. Além disso, você não pode definir esta propriedade como false para tipos de valores anuláveis. Quando esses tipos são nulos, eles serão serializados configurando xsi: nil como true.
bouvierr
12

Você pode definir alguns valores padrão e isso impede que os campos sejam serializados.

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

    [XmlArray, DefaultValue(null)]
    List<string> data;
MichaelSo
fonte
Infelizmente, isso não funciona para tipos de valor anuláveis
bubi
2

Prefiro criar meu próprio xml sem tags geradas automaticamente. Nisto posso ignorar a criação de nós com 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
fonte
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
fonte
1

No meu caso, as variáveis ​​/ elementos anuláveis ​​eram todos do tipo String. Então, eu simplesmente executei uma verificação e atribuí a eles string.Empty no caso de NULL. Dessa forma, me livrei dos atributos nil e xmlns desnecessários (p3: nil = "true" xmlns: p3 = "http://www.w3.org/2001/XMLSchema-instance)

// Example:

myNullableStringElement = varCarryingValue ?? string.Empty

// OR

myNullableStringElement = myNullableStringElement ?? string.Empty
Sagar
fonte
1
Esta solução é muito limitada e funciona apenas com string. Para outros tipos, a sequência vazia ainda é um valor. Alguns analisadores tentam encontrar o atributo e, se encontrado, tentam converter o valor no tipo de destino. Para esses analisadores, atributo ausente significa nulo e, se houver atributo, ele deverá ter um valor válido.
ZafarYousafi