XML de serialização XML?

Eu me deparei com algumas pegadinhas ao fazer serialização C # XML que eu pensei em compartilhar:

  • Você não pode serializar itens que são somente leitura (como KeyValuePairs)
  • Você não pode serializar um dictionary genérico. Em vez disso, tente esta class wrapper (de http://weblogs.asp.net/pwelter34/archive/2006/05/03/444961.aspx ):

using System; using System.Collections.Generic; using System.Text; using System.Xml.Serialization; [XmlRoot("dictionary")] public class SerializableDictionary : Dictionary, 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(); } } } 

Algum outro object de serialização XML lá fora?

Outra grande pegadinha: ao gerar XML através de uma página da Web (ASP.NET), você não deseja include a Marca de Ordem de Byte Unicode . Naturalmente, as formas de usar ou não a lista de materiais são quase as mesmas:

BAD (inclui BOM):

 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 você não deseja a lista de materiais. Observe a clara e óbvia diferença entre o Encoding.UTF8 e o UTF8Encoding .

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

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

Ainda não posso fazer comentários, então vou comentar o post do Dr8k e fazer outra observação. Variáveis ​​privadas que são expostas como propriedades get / setter públicas e são serializadas / desserializadas como tais através dessas propriedades. Nós fizemos isso no meu antigo emprego o tempo todo.

Uma coisa a notar é que se você tem alguma lógica nessas propriedades, a lógica é executada, então, às vezes, a ordem de serialização realmente importa. Os membros são implicitamente ordenados por como eles são ordenados no código, mas não há garantias, especialmente quando você está herdando outro object. Ordená-los explicitamente é uma dor na parte traseira.

Eu fui queimado por isso no passado.

Ao serializar em uma cadeia XML de um stream de memory, certifique-se de usar MemoryStream # ToArray () em vez de MemoryStream # GetBuffer () ou você terminará com caracteres indesejados que não serão desserializados corretamente (devido ao buffer extra alocado).

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

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

IEnumerables que são gerados por meio de retornos de rendimento não são serializáveis. Isso ocorre porque o compilador gera uma class separada para implementar o retorno de rendimento e essa class não está marcada como serializável.

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 o XML em um object.

Pela mesma razão, você não pode serializar propriedades que retornam interfaces: o desserializador não saberia qual class concreta instanciar.

Ah, aqui vai uma boa: já que 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 em seu código que quebra o serializador. Apenas algo como “incapaz de localizar s3d3fsdf.dll”. Agradável.

Uma coisa mais a notar: você não pode serializar membros da class privada / protegida se você estiver usando a serialização XML “padrão”.

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

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

Não é possível serializar um object que não tenha um construtor sem parâmetros (acabou de ser mordido por esse).

E por algum motivo, nas seguintes propriedades, o valor fica serializado, mas não FullName:

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

Eu nunca me preocupei em descobrir porque, eu apenas mudei o valor para interno …

Se o seu assembly gerado XML Serialization não estiver no mesmo contexto Load que o código que está tentando usá-lo, você terá 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 plugin carregado usando o contexto LoadFrom, que tem muitas desvantagens em usar o contexto Load. Muito divertido rastrear isso.

Você pode enfrentar problemas ao serializar objects 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

Consulte ” Suporte avançado à vinculação de atributos de linguagem de definição de esquema XML ” para obter detalhes sobre o que é suportado pelo XML Serializer e para obter detalhes sobre como os resources suportados do XSD são suportados.

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

Aqui faz parte de um exemplo completo da 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; 

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

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.

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

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

mas isso vai:

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

E também vale a pena notar que, se você for serializar para um memstream, talvez você queira procurar 0 antes de usá-lo.

Seja cuidadoso com os tipos de serialização sem serialização explícita, isso pode resultar em atrasos enquanto o .Net os constrói. Eu descobri isso recentemente durante a serialização de RSAParameters .

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

Por exemplo.

                          

Neste exemplo, um envelope pode conter mensagens. No entanto, o serializador padrão do .NET não distingue entre Message, ExampleMessageA e ExampleMessageB. Ele só será serializado de e para a class Message base.

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

Acredito que isso também aconteça se você estiver expondo os membros privados por meio de propriedades públicas – os membros privados não são serializados para que os membros públicos fiquem referenciando valores nulos.