it-swarm-pt.tech

Pegadinhas de serialização XML do .NET?

Encontrei algumas dicas ao fazer serialização XML em C # que pensei em compartilhar:


using System;
using System.Collections.Generic;
using System.Text;
using System.Xml.Serialization;

[XmlRoot("dictionary")]
public class SerializableDictionary<TKey, TValue> : Dictionary<TKey, TValue>, IXmlSerializable
{      
    public System.Xml.Schema.XmlSchema GetSchema()
    {
        return null;
    }

    public void ReadXml(System.Xml.XmlReader reader)
    {
        XmlSerializer keySerializer = new XmlSerializer(typeof(TKey));
        XmlSerializer valueSerializer = new XmlSerializer(typeof(TValue));

        bool wasEmpty = reader.IsEmptyElement;
        reader.Read();

        if (wasEmpty)
            return;

        while (reader.NodeType != System.Xml.XmlNodeType.EndElement)
        {
            reader.ReadStartElement("item");

            reader.ReadStartElement("key");
            TKey key = (TKey)keySerializer.Deserialize(reader);
            reader.ReadEndElement();

            reader.ReadStartElement("value");
            TValue value = (TValue)valueSerializer.Deserialize(reader);
            reader.ReadEndElement();

            this.Add(key, value);

            reader.ReadEndElement();
            reader.MoveToContent();
        }
        reader.ReadEndElement();
    }

    public void WriteXml(System.Xml.XmlWriter writer)
    {
        XmlSerializer keySerializer = new XmlSerializer(typeof(TKey));
        XmlSerializer valueSerializer = new XmlSerializer(typeof(TValue));

        foreach (TKey key in this.Keys)
        {
            writer.WriteStartElement("item");

            writer.WriteStartElement("key");
            keySerializer.Serialize(writer, key);
            writer.WriteEndElement();

            writer.WriteStartElement("value");
            TValue value = this[key];
            valueSerializer.Serialize(writer, value);
            writer.WriteEndElement();

            writer.WriteEndElement();
        }
    }
}

Alguma outra dica de serialização XML por aí?

120
Kalid

Outra grande dificuldade: ao gerar XML através de uma página da Web (ASP.NET), você não deseja incluir o Marca de Pedido de Byte Unicode . Obviamente, as maneiras de usar ou não a BOM são quase as mesmas:

RUIM (inclui lista técnica):

XmlTextWriter wr = new XmlTextWriter(stream, new System.Text.Encoding.UTF8);

BOA:

XmlTextWriter  wr = new XmlTextWriter(stream, new System.Text.UTF8Encoding(false))

Você pode explicitamente passar false para indicar que não deseja a BOM. Observe a clara e óbvia diferença entre Encoding.UTF8 e UTF8Encoding.

Os três bytes extras de BOM no início são (0xEFBBBF) ou (239 187 191).

Referência: http://chrislaco.com/blog/trou Troubleshooting-common-problems-with-the-xmlserializer/

27
Kalid

Ainda não posso fazer comentários, então comentarei o post do Dr8k e farei outra observação. Variáveis ​​privadas que são expostas como propriedades públicas de getter/setter e são serializadas/desserializadas como tais por meio dessas propriedades. Fizemos isso no meu antigo emprego o tempo todo.

Porém, uma coisa a ser observada é que, se você tiver alguma lógica nessas propriedades, a lógica será executada; portanto, às vezes, a ordem da serialização é realmente importante. Os membros são implicitamente ordenados pela maneira como são ordenados no código, mas não há garantias, especialmente quando você está herdando outro objeto. Ordená-los explicitamente é uma dor na parte traseira.

Eu fui queimado por isso no passado.

21
Charles Graham

Ao serializar em uma sequência XML a partir de um fluxo de memória, use MemoryStream # ToArray () em vez de MemoryStream # GetBuffer () ou você terminará com caracteres indesejados que não serão desserializados adequadamente (devido ao buffer extra alocado).

http://msdn.Microsoft.com/en-us/library/system.io.memorystream.getbuffer (VS.80) .aspx

15
realgt

IEnumerables<T> gerados através de retornos de rendimento não são serializáveis. Isso ocorre porque o compilador gera uma classe separada para implementar retorno de rendimento e essa classe não é marcada como serializável.

10
bwillard

Se o serializador encontrar um membro/propriedade que tenha uma interface como seu tipo, ele não será serializado. Por exemplo, o seguinte não será serializado para XML:

public class ValuePair
{
    public ICompareable Value1 { get; set; }
    public ICompareable Value2 { get; set; }
}

Embora isso seja serializado:

public class ValuePair
{
    public object Value1 { get; set; }
    public object Value2 { get; set; }
}
10
Allon Guralnek

Você não pode serializar propriedades somente leitura. Você deve ter um getter e um setter, mesmo que nunca pretenda usar a desserialização para transformar XML em um objeto.

Pelo mesmo motivo, você não pode serializar propriedades que retornam interfaces: o desserializador não saberia qual classe concreta instanciar.

8
Tim Robinson

Ah, eis uma boa: como o código de serialização XML é gerado e colocado em uma DLL separada, você não recebe nenhum erro significativo quando há um erro no seu código que interrompe o serializador. Apenas algo como "incapaz de localizar s3d3fsdf.dll". Agradável.

7
Eric Z Beard

Não é possível serializar um objeto que não possui um construtor sem parâmetros (apenas foi mordido por esse).

E, por algum motivo, nas seguintes propriedades, o Value é serializado, mas não o FullName:

    public string FullName { get; set; }
    public double Value { get; set; }

Nunca resolvi o porquê, apenas mudei Value para interno ...

6
Benjol

Mais uma observação: você não pode serializar membros da classe privada/protegida se estiver usando a serialização XML "padrão".

Mas você pode especificar uma lógica de serialização XML personalizada implementando IXmlSerializable em sua classe e serializar todos os campos particulares que você precisa/deseja.

http://msdn.Microsoft.com/en-us/library/system.xml.serialization.ixmlserializable.aspx

5
Max Galkin

Você pode enfrentar problemas para serializar objetos do tipo Cor e/ou Fonte.

Aqui estão os conselhos que me ajudaram:

http://www.codeproject.com/KB/XML/xmlsettings.aspx

http://www.codeproject.com/KB/cs/GenericXmlSerializition.aspx

4
Max Galkin

Consulte " Suporte à Vinculação de Atributos da Linguagem de Definição de Esquema XML Avançado " para obter detalhes sobre o que é suportado pelo serializador de XML e para obter detalhes sobre a maneira como os recursos XSD suportados são suportados.

4
John Saunders

Se você tentar serializar uma matriz, List<T> ou IEnumerable<T>, que contém instâncias de subclasses de T, você precisa usar o XmlArrayItemAttribute para listar todos os subtipos que estão sendo usados. Caso contrário, você obterá um System.InvalidOperationException inútil em tempo de execução ao serializar.

Aqui está parte de um exemplo completo de a documentação

public class Group
{  
   /* The XmlArrayItemAttribute allows the XmlSerializer to insert both the base 
      type (Employee) and derived type (Manager) into serialized arrays. */

   [XmlArrayItem(typeof(Manager)), XmlArrayItem(typeof(Employee))]
   public Employee[] Employees;
4
MarkJ

Se o assembly gerado por serialização XML não estiver no mesmo contexto de carregamento que o código que está tentando usá-lo, você encontrará erros impressionantes, como:

System.InvalidOperationException: There was an error generating the XML document.
---System.InvalidCastException: Unable to cast object
of type 'MyNamespace.Settings' to type 'MyNamespace.Settings'. at
Microsoft.Xml.Serialization.GeneratedAssembly.
  XmlSerializationWriterSettings.Write3_Settings(Object o)

A causa disso para mim foi um plug-in carregado usando contexto LoadFrom , que tem muitas desvantagens em usar o contexto Load. Um pouco divertido rastrear esse.

4
user7116

As propriedades marcadas com o atributo Obsolete não são serializadas. Eu não testei com o atributo Deprecated, mas presumo que funcionaria da mesma maneira.

3
James Hulse

Variáveis ​​/ propriedades privadas não são serializadas no mecanismo padrão para serialização XML, mas estão na serialização binária.

3
Charles Graham

Se o seu XSD faz uso de grupos de substituição, é provável que você não possa (des) serializá-lo automaticamente. Você precisará escrever seus próprios serializadores para lidar com esse cenário.

Por exemplo.

<xs:complexType name="MessageType" abstract="true">
    <xs:attributeGroup ref="commonMessageAttributes"/>
</xs:complexType>

<xs:element name="Message" type="MessageType"/>

<xs:element name="Envelope">
    <xs:complexType mixed="false">
        <xs:complexContent mixed="false">
            <xs:element ref="Message" minOccurs="0" maxOccurs="unbounded"/>
        </xs:complexContent>
    </xs:complexType>
</xs:element>

<xs:element name="ExampleMessageA" substitutionGroup="Message">
    <xs:complexType mixed="false">
        <xs:complexContent mixed="false">
                <xs:attribute name="messageCode"/>
        </xs:complexContent>
    </xs:complexType>
</xs:element>

<xs:element name="ExampleMessageB" substitutionGroup="Message">
    <xs:complexType mixed="false">
        <xs:complexContent mixed="false">
                <xs:attribute name="messageCode"/>
        </xs:complexContent>
    </xs:complexType>
</xs:element>

Neste exemplo, um envelope pode conter mensagens. No entanto, o serializador padrão do .NET não distingue entre Message, ExampleMessageA e ExampleMessageB. Serializará apenas de e para a classe Message base.

2
ilitirit

Tenha cuidado com os tipos de serialização sem serialização explícita, pois isso pode resultar em atrasos enquanto o .Net os cria. Eu descobri isso recentemente durante a serialização de RSAParameters .

2
Keith

Eu realmente não posso explicar este, mas achei que isso não seria serializado:

[XmlElement("item")]
public myClass[] item
{
    get { return this.privateList.ToArray(); }
}

mas isso irá:

[XmlElement("item")]
public List<myClass> item
{
    get { return this.privateList; }
}

E também vale a pena notar que, se você estiver serializando para um fluxo de memórias, convém procurar 0 antes de usá-lo.

2
annakata

Variáveis ​​/ propriedades privadas não são serializadas na serialização XML, mas estão na serialização binária.

Acredito que isso também o faça se você estiver expondo os membros privados por meio de propriedades públicas - os membros privados não são serializados, portanto todos os membros públicos estão fazendo referência a valores nulos.

0
Dr8k