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>

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>

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;
}

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.


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;

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;
    }

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;
}

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

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
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.