Observação
O acesso a essa página exige autorização. Você pode tentar entrar ou alterar diretórios.
O acesso a essa página exige autorização. Você pode tentar alterar os diretórios.
O Windows Communication Foundation (WCF) pode usar duas tecnologias de serialização diferentes para transformar os dados em seu aplicativo em XML transmitidos entre clientes e serviços: DataContractSerializer e XmlSerializer.
DataContractSerializer
Por padrão, o WCF usa a DataContractSerializer classe para serializar tipos de dados. Esse serializador dá suporte aos seguintes tipos:
Tipos primitivos (por exemplo, inteiros, cadeias de caracteres e matrizes de bytes), bem como alguns tipos especiais, como XmlElement e DateTime, que são tratados como primitivos.
Tipos de contrato de dados (tipos marcados com o DataContractAttribute atributo).
Tipos marcados com o SerializableAttribute atributo, que incluem tipos que implementam a ISerializable interface.
Tipos que implementam a interface IXmlSerializable.
Diversos tipos de coleções comuns, incluindo várias coleções genéricas.
Muitos tipos do .NET Framework se enquadram nas duas últimas categorias e, portanto, são serializáveis. Matrizes de tipos serializáveis também são serializáveis. Para obter uma lista completa, consulte Especificando a transferência de dados em contratos de serviço.
O DataContractSerializer, usado junto com tipos de contrato de dados, é a maneira recomendada de escrever novos serviços WCF. Para obter mais informações, consulte Como usar contratos de dados.
XmlSerializer
O WCF também dá suporte à XmlSerializer classe. A XmlSerializer classe não é exclusiva do WCF. É o mesmo mecanismo de serialização que os serviços Web ASP.NET usam. A XmlSerializer classe dá suporte a um conjunto de tipos muito mais estreito do que a DataContractSerializer classe, mas permite muito mais controle sobre o XML resultante e dá suporte a muito mais do padrão XSD (linguagem de definição de esquema XML). Ele também não requer atributos declarativos nos tipos serializáveis. Para obter mais informações, consulte o tópico serialização XML na documentação do .NET Framework. A XmlSerializer classe não dá suporte a tipos de contrato de dados.
Ao usar Svcutil.exe ou o recurso Adicionar Referência de Serviço no Visual Studio para gerar o código do cliente para um serviço de terceiros ou para acessar um esquema de terceiros, um serializador apropriado é selecionado automaticamente para você. Se o esquema não for compatível com o DataContractSerializer, o XmlSerializer será selecionado.
Mudar para o XmlSerializer
Às vezes, talvez seja necessário alternar manualmente para o XmlSerializer. Isso acontece, por exemplo, nos seguintes casos:
Ao migrar um aplicativo de serviços Web do ASP.NET para o WCF, convém reutilizar tipos existentes convertíveis a XmlSerializer em vez de criar novos tipos de contrato de dados.
Quando o controle preciso sobre o XML que aparece em mensagens é importante, mas um documento WSDL (Linguagem de Descrição dos Serviços Web) não está disponível, por exemplo, ao criar um serviço com tipos que precisam estar em conformidade com um determinado esquema padronizado e publicado que não seja compatível com o DataContractSerializer.
Ao criar serviços que seguem o padrão de codificação SOAP herdado.
Nesses e em outros casos, você pode alternar manualmente para a XmlSerializer classe aplicando o XmlSerializerFormatAttribute atributo ao serviço, conforme mostrado no código a seguir.
[ServiceContract]
[XmlSerializerFormat]
public class BankingService
{
[OperationContract]
public void ProcessTransaction(BankingTransaction bt)
{
// Code not shown.
}
}
//BankingTransaction is not a data contract class,
//but is an XmlSerializer-compatible class instead.
public class BankingTransaction
{
[XmlAttribute]
public string Operation;
[XmlElement]
public Account fromAccount;
[XmlElement]
public Account toAccount;
[XmlElement]
public int amount;
}
//Notice that the Account class must also be XmlSerializer-compatible.
<ServiceContract(), XmlSerializerFormat()> _
Public Class BankingService
<OperationContract()> _
Public Sub ProcessTransaction(ByVal bt As BankingTransaction)
' Code not shown.
End Sub
End Class
' BankingTransaction is not a data contract class,
' but is an XmlSerializer-compatible class instead.
Public Class BankingTransaction
<XmlAttribute()> _
Public Operation As String
<XmlElement()> _
Public fromAccount As Account
<XmlElement()> _
Public toAccount As Account
<XmlElement()> _
Public amount As Integer
End Class
'Notice that the Account class must also be XmlSerializer-compatible.
Considerações de segurança
Observação
É importante ter cuidado ao alternar mecanismos de serialização. O mesmo tipo pode serializar para XML de forma diferente dependendo do serializador que está sendo usado. Se você usar acidentalmente o serializador errado, poderá estar divulgando informações do tipo que não pretende divulgar.
Por exemplo, a DataContractSerializer classe só serializa membros marcados com o DataMemberAttribute atributo ao serializar tipos de contrato de dados. A XmlSerializer classe serializa qualquer membro público. Veja o tipo no código a seguir.
[DataContract]
public class Customer
{
[DataMember]
public string firstName;
[DataMember]
public string lastName;
public string creditCardNumber;
}
<DataContract()> _
Public Class Customer
<DataMember()> _
Public firstName As String
<DataMember()> _
Public lastName As String
Public creditCardNumber As String
End Class
Se o tipo for usado inadvertidamente em um contrato de serviço onde a classe XmlSerializer é selecionada, o membro creditCardNumber será serializado, o que provavelmente não é o desejado.
Mesmo que a DataContractSerializer classe seja o padrão, você pode selecioná-la explicitamente para seu serviço (embora isso nunca deva ser necessário) aplicando o DataContractFormatAttribute atributo ao tipo de contrato de serviço.
O serializador usado para o serviço é parte integrante do contrato e não pode ser alterado selecionando uma associação diferente ou alterando outras configurações.
Outras considerações de segurança importantes se aplicam à XmlSerializer classe. Primeiro, é altamente recomendável que qualquer aplicativo WCF que use a XmlSerializer classe seja assinado com uma chave protegida contra a divulgação. Essa recomendação se aplica tanto quando uma mudança manual para XmlSerializer é realizada quanto quando uma mudança automática é realizada (por Svcutil.exe, Adicionar Referência de Serviço ou uma ferramenta semelhante). Isso ocorre porque o mecanismo de serialização XmlSerializer dá suporte ao carregamento de assemblies de serialização pré-gerados, desde que sejam assinados com a mesma chave que o aplicativo. Um aplicativo sem sinal é completamente desprotegido da possibilidade de um assembly mal-intencionado corresponder ao nome esperado do assembly de serialização pré-gerado que está sendo colocado na pasta do aplicativo ou no cache de assembly global. É claro que um invasor deve primeiro obter acesso de gravação a um desses dois locais para tentar essa ação.
Outra ameaça que existe sempre que você usa XmlSerializer está relacionada ao acesso de gravação à pasta temporária do sistema. O mecanismo de serialização XmlSerializer cria e usa assemblies de serialização temporários nessa pasta. Você deve estar ciente de que qualquer processo com acesso de gravação à pasta temporária pode substituir esses assemblies de serialização com código mal-intencionado.
Regras para suporte ao XmlSerializer
Não é possível aplicar tributos compatíveis com XmlSerializer diretamente a parâmetros de operação de contrato ou valores retornados. No entanto, elas podem ser aplicadas a mensagens digitadas (partes do corpo do contrato de mensagem), conforme mostrado no código a seguir.
[ServiceContract]
[XmlSerializerFormat]
public class BankingService
{
[OperationContract]
public void ProcessTransaction(BankingTransaction bt)
{
//Code not shown.
}
}
[MessageContract]
public class BankingTransaction
{
[MessageHeader]
public string Operation;
[XmlElement, MessageBodyMember]
public Account fromAccount;
[XmlElement, MessageBodyMember]
public Account toAccount;
[XmlAttribute, MessageBodyMember]
public int amount;
}
<ServiceContract(), XmlSerializerFormat()> _
Public Class BankingService
<OperationContract()> _
Public Sub ProcessTransaction(ByVal bt As BankingTransaction)
'Code not shown.
End Sub
End Class
<MessageContract()> _
Public Class BankingTransaction
<MessageHeader()> _
Public Operation As String
<XmlElement(), MessageBodyMember()> _
Public fromAccount As Account
<XmlElement(), MessageBodyMember()> _
Public toAccount As Account
<XmlAttribute(), MessageBodyMember()> _
Public amount As Integer
End Class
Quando aplicados a membros de mensagens tipadas, esses atributos substituem as propriedades que conflitam nos atributos de mensagens tipadas. Por exemplo, no código a seguir, ElementName substitui Name.
[MessageContract]
public class BankingTransaction
{
[MessageHeader] public string Operation;
//This element will be <fromAcct> and not <from>:
[XmlElement(ElementName="fromAcct"), MessageBodyMember(Name="from")]
public Account fromAccount;
[XmlElement, MessageBodyMember]
public Account toAccount;
[XmlAttribute, MessageBodyMember]
public int amount;
}
<MessageContract()> _
Public Class BankingTransaction
<MessageHeader()> _
Public Operation As String
'This element will be <fromAcct> and not <from>:
<XmlElement(ElementName:="fromAcct"), _
MessageBodyMember(Name:="from")> _
Public fromAccount As Account
<XmlElement(), MessageBodyMember()> _
Public toAccount As Account
<XmlAttribute(), MessageBodyMember()> _
Public amount As Integer
End Class
O atributo MessageHeaderArrayAttribute não é suportado ao usar o XmlSerializer.
Observação
Nesse caso, gera a XmlSerializer seguinte exceção, que é lançada antes do WCF: "Um elemento declarado no nível superior de um esquema não pode ter maxOccurs> 1. Forneça um elemento wrapper para 'mais' usando XmlArray ou XmlArrayItem em vez de XmlElementAttribute, ou usando o estilo de parâmetro Wrapped".
Se você receber essa exceção, investigue se essa situação se aplica.
O WCF não dá suporte a SoapIncludeAttribute e XmlIncludeAttribute atributos em contratos de mensagens e contratos de operação; use o KnownTypeAttribute atributo em vez disso.
Tipos que implementam a interface IXmlSerialable
Os tipos que implementam a IXmlSerializable interface são totalmente compatíveis com o DataContractSerializer. O XmlSchemaProviderAttribute atributo sempre deve ser aplicado a esses tipos para controlar seu esquema.
Aviso
Se você estiver serializando tipos polimórficos, deverá aplicar o XmlSchemaProviderAttribute ao tipo para garantir que o tipo correto seja serializado.
Há três variedades de tipos que implementam IXmlSerializable: tipos que representam conteúdo arbitrário, tipos que representam um único elemento e tipos herdados DataSet .
Os tipos de conteúdo usam um método de provedor de esquema especificado pelo
XmlSchemaProviderAttributeatributo. O método não retornanulle a IsAny propriedade no atributo é deixada no valor padrão defalse. Esse é o uso mais comum deIXmlSerializabletipos.Os tipos de elemento são usados quando um
IXmlSerializabletipo deve controlar seu próprio nome de elemento raiz. Para marcar um tipo como um tipo de elemento, defina a propriedade IsAny no atributo XmlSchemaProviderAttribute comotrueou retornenulldo método do provedor de esquema. Ter um método de provedor de esquema é opcional para tipos de elementos– você pode especificarnullem vez do nome do método noXmlSchemaProviderAttribute. No entanto, seIsAnyfortruee um método de provedor de esquema for especificado, o método deverá retornarnull.Tipos herdados DataSet são tipos
IXmlSerializableque não são marcados com o atributoXmlSchemaProviderAttribute. Em vez disso, eles dependem do GetSchema método para a geração de esquema. Esse padrão é usado para o tipoDataSete seu conjunto de dados tipado deriva uma classe em versões anteriores do .NET Framework, mas agora está obsoleto e tem compatibilidade apenas por motivos herdados. Não confie nesse padrão e sempre aplique oXmlSchemaProviderAttributeaos seus tipos deIXmlSerializable.
Tipos de conteúdo IXmlSerializable
Ao serializar um membro de dados de um tipo que implementa IXmlSerializable e é um tipo de conteúdo, conforme definido anteriormente, o serializador grava o elemento wrapper para o membro de dados e passa o controle para o método WriteXml. A WriteXml implementação pode gravar qualquer XML, o que inclui a adição de atributos ao elemento wrapper. Depois que WriteXml é concluído, o serializador fecha o elemento.
Ao desserializar um membro de dados de um tipo que implementa IXmlSerializable e é um tipo de conteúdo conforme definido anteriormente, o desserializador posiciona o leitor XML no elemento wrapper do membro de dados e passa o controle para o ReadXml método. O método deve ler todo o elemento, incluindo as marcas inicial e final. Verifique se o ReadXml código manipula o caso em que o elemento está vazio. Além disso, sua implementação de ReadXml não deve depender do elemento wrapper que está sendo nomeado de uma maneira específica. O nome escolhido pelo serializador pode variar.
É permitido atribuir IXmlSerializable tipos de conteúdo polimorficamente, por exemplo, a membros de dados do tipo Object. Também é permitido que as instâncias de tipo sejam nulas. Por fim, é possível usar tipos IXmlSerializable com a preservação do grafo de objetos habilitada e com o NetDataContractSerializer. Todos esses recursos exigem que o serializador WCF anexe determinados atributos ao elemento de invólucro ("nil" e "type" no namespace da Instância de Esquema XML e "Id", "Ref", "Type" e "Assembly" em um namespace específico do WCF).
Atributos a serem ignorados ao implementar ReadXml
Antes de passar o controle para o código ReadXml, o desserializador examina o elemento XML, detecta esses atributos XML especiais e atua neles. Por exemplo, se "nil" for true, um valor nulo será desserializado e ReadXml não será chamado. Se o polimorfismo for detectado, o conteúdo do elemento será desserializado como se fosse um tipo diferente. A implementação do tipo atribuído polimorficamente de ReadXml é chamada. De qualquer forma, uma ReadXml implementação deve ignorar esses atributos especiais porque eles são tratados pelo desserializador.
Considerações de esquema para tipos de conteúdo IXmlSerializable
Ao exportar o esquema de um tipo de conteúdo IXmlSerializable, o método do provedor de esquema é chamado. Um XmlSchemaSet é passado para o método do provedor de esquema. O método pode adicionar qualquer esquema válido ao conjunto de esquemas. O conjunto de esquemas contém o esquema que já é conhecido no momento em que ocorre a exportação de esquema. Quando o método do provedor de esquema deve adicionar um item ao conjunto de esquemas, ele deve determinar se já existe um XmlSchema namespace com o namespace apropriado no conjunto. Se isso acontecer, o método do provedor de esquema deverá adicionar o novo item ao existente XmlSchema. Caso contrário, ele deve criar uma nova XmlSchema instância. Isso é importante se matrizes de IXmlSerializable tipos estiverem sendo usadas. Por exemplo, se você tiver um tipo IXmlSerializable exportado como tipo "A" no namespace "B", é possível que, quando o método do provedor de esquema for chamado, o esquema já contenha o esquema de "B" para manter o tipo "ArrayOfA".
Além de adicionar tipos ao XmlSchemaSet, o método do provedor de esquema para tipos de conteúdo deve retornar um valor não nulo. Ele pode retornar um XmlQualifiedName que especifica o nome do tipo de esquema a ser usado para o tipo fornecido IXmlSerializable . Esse nome qualificado também serve como o nome e o namespace do contrato de dados para o tipo. É permitido retornar um tipo que não existe no conjunto de esquemas imediatamente quando o método do provedor de esquema retorna. No entanto, espera-se que, no momento em que todos os tipos relacionados são exportados (o Export método é chamado para todos os tipos relevantes no XsdDataContractExporter e a Schemas propriedade é acessada), o tipo existe no conjunto de esquemas. Acessar a propriedade Schemas antes que todas as chamadas Export relevantes sejam feitas pode resultar em um XmlSchemaException. Para obter mais informações sobre o processo de exportação, consulte Exportando esquemas de classes.
O método do provedor de esquema também pode retornar o XmlSchemaType para usar. O tipo pode ou não ser anônimo. Se for anônimo, o esquema do IXmlSerializable tipo será exportado como um tipo anônimo sempre que o IXmlSerializable tipo for usado como um membro de dados. O tipo IXmlSerializable ainda tem um nome de contrato de dados e um namespace. (Isso é determinado conforme descrito em Nomes de Contrato de Dados , exceto que o DataContractAttribute atributo não pode ser usado para personalizar o nome.) Se não for anônimo, deve ser um dos tipos no XmlSchemaSet. Esse caso é equivalente a retornar o XmlQualifiedName do tipo.
Além disso, uma declaração de elemento global é exportada para o tipo. Se o tipo não tiver o XmlRootAttribute atributo aplicado a ele, o elemento terá o mesmo nome e namespace que o contrato de dados e sua propriedade "nillable" será true. A única exceção a isso é o namespace de esquema (http://www.w3.org/2001/XMLSchema) – se o contrato de dados do tipo estiver nesse namespace, o elemento global correspondente estará no namespace em branco porque é proibido adicionar novos elementos ao namespace de esquema. Se o tipo tiver o XmlRootAttribute atributo aplicado a ele, a declaração de elemento global será exportada usando o seguinte: ElementNamee NamespaceIsNullable propriedades. Os padrões com XmlRootAttribute aplicados são o nome do contrato de dados, um namespace em branco e "nillable" sendo true.
As mesmas regras de declaração de elemento global se aplicam aos tipos de conjunto de dados herdados. Observe que o XmlRootAttribute não pode substituir as declarações de elementos globais adicionadas por meio de código personalizado, seja utilizando o método do provedor de esquema no XmlSchemaSet ou através do GetSchema para tipos de conjuntos de dados legados.
Tipos de elemento IXmlSerializable
os tipos de elemento IXmlSerializable têm a propriedade IsAny definida como true ou têm o método de provedor de esquema retornado null.
Serializar e desserializar um tipo de elemento é muito semelhante à serialização e desserialização de um tipo de conteúdo. No entanto, há algumas diferenças importantes:
Espera-se que a implementação de
WriteXmlgrave exatamente um elemento (que, naturalmente, poderia conter vários elementos filho). Ele não deve escrever atributos fora desse único elemento, vários elementos irmãos ou conteúdo misto. O elemento pode estar vazio.A
ReadXmlimplementação não deve ler o elemento wrapper. Espera-se que ele leia o único elemento queWriteXmlproduz.Ao serializar um tipo de elemento com frequência (por exemplo, como um membro de dados em um contrato de dados), o serializador gera um elemento envoltório antes de chamar
WriteXml, tal como ocorre com tipos de conteúdo. No entanto, ao serializar um tipo de elemento no nível superior, o serializador normalmente não produz um elemento wrapper ao redor do elemento queWriteXmlescreve, a menos que um nome raiz e um namespace sejam explicitamente especificados ao construir o serializador nos construtoresDataContractSerializerouNetDataContractSerializer. Para obter mais informações, consulte Serialização e Desserialização.Ao serializar um tipo de elemento no nível superior sem especificar o nome raiz e o namespace em tempo de construção, WriteStartObject e WriteEndObject essencialmente não fazem nada e WriteObjectContent chama
WriteXml. Nesse modo, o objeto que está sendo serializado não pode sernulle não pode ser atribuído polimorficamente. Além disso, a preservação do grafo de objetos não pode ser habilitada e oNetDataContractSerializernão pode ser usado.Ao desserializar um tipo de elemento no nível superior sem especificar o nome raiz e o namespace durante a construção, IsStartObject retorna
truese puder encontrar o início de qualquer elemento. ReadObject com o parâmetroverifyObjectNameconfigurado comotruecomporta-se da mesma maneira queIsStartObjectantes de realmente ler o objeto.ReadObjectem seguida, passa o controle para oReadXmlmétodo.
O esquema exportado para tipos de elemento é o mesmo que para o tipo XmlElement, conforme descrito em uma seção anterior, exceto que o método do provedor de esquema pode adicionar qualquer esquema adicional ao XmlSchemaSet, assim como acontece com tipos de conteúdo. O uso do XmlRootAttribute atributo com tipos de elemento não é permitido e declarações de elemento global nunca são emitidas para esses tipos.
Diferenças do XmlSerializer
A IXmlSerializable interface e os XmlSchemaProviderAttributeXmlRootAttribute atributos também são compreendidos pelo XmlSerializer . No entanto, há algumas diferenças na forma como elas são tratadas no modelo de contrato de dados. As diferenças importantes são resumidas na seguinte lista:
O método de provedor de esquema deve ser público para ser usado no
XmlSerializer, mas não precisa ser público para ser usado no modelo de contrato de dados.O método do provedor de esquema é chamado quando
IsAnyétrueno modelo de contrato de dados, mas não com oXmlSerializer.Quando o
XmlRootAttributeatributo não está presente para tipos de conteúdo ou conjunto de dados herdados, oXmlSerializerelemento exporta uma declaração de elemento global no namespace em branco. No modelo de contrato de dados, o namespace usado normalmente é o namespace do contrato de dados, conforme descrito anteriormente.
Lembre-se dessas diferenças ao criar tipos usados com ambas as tecnologias de serialização.
Importando esquema IXmlSerializable
Ao importar um esquema gerado de IXmlSerializable tipos, há algumas possibilidades:
O esquema gerado pode ser um esquema de contrato de dados válido, conforme descrito na Referência de Esquema de Contrato de Dados. Nesse caso, o esquema pode ser importado como de costume e tipos de contrato de dados regulares são gerados.
O esquema gerado pode não ser um esquema de contrato de dados válido. Por exemplo, seu método de provedor de esquema pode gerar um esquema que envolve atributos XML que não têm suporte no modelo de contrato de dados. Nesse caso, você pode importar o esquema como
IXmlSerializabletipos. Esse modo de importação não está ativado por padrão, mas pode ser facilmente habilitado– por exemplo, com a opção/importXmlTypesde linha de comando para a Ferramenta de Utilitário de Metadados do ServiceModel (Svcutil.exe). Isso é descrito em detalhes no esquema de importação para gerar classes. Observe que você deve trabalhar diretamente com o XML para suas instâncias de tipo. Você também pode considerar o uso de uma tecnologia de serialização diferente que dá suporte a uma gama mais ampla de esquemas – consulte o tópico sobre como usar oXmlSerializer.Talvez você queira reutilizar seus tipos existentes
IXmlSerializableno proxy em vez de gerar novos. Nesse caso, o recurso de tipos referenciados descrito no tópico Importar Esquema para Gerar Tipos pode ser usado para indicar o tipo a ser reutilizado. Isso corresponde ao uso da opção/referenceno svcutil.exe, que especifica o assembly que contém os tipos a serem reutilizados.
Comportamento herdado do XmlSerializer
No .NET Framework 4.0 e anterior, o XmlSerializer gerou assemblies de serialização temporários escrevendo código C# em um arquivo. Em seguida, o arquivo foi compilado em um assembly. Esse comportamento teve algumas consequências indesejáveis, como diminuir o tempo de inicialização do serializador. No .NET Framework 4.5, esse comportamento foi alterado para gerar os assemblies sem exigir o uso do compilador. Alguns desenvolvedores podem querer ver o código C# gerado. Você pode especificar para usar esse comportamento herdado pela seguinte configuração:
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<system.xml.serialization>
<xmlSerializer tempFilesLocation='e:\temp\XmlSerializerBug' useLegacySerializerGeneration="true" />
</system.xml.serialization>
<system.diagnostics>
<switches>
<add name="XmlSerialization.Compilation" value="1" />
</switches>
</system.diagnostics>
</configuration>
Se você encontrar problemas de compatibilidade, como a falha de XmlSerializer em serializar uma classe derivada com uma nova substituição não pública, poderá voltar para o comportamento herdado XMLSerializer usando a seguinte configuração:
<configuration>
<appSettings>
<add key="System:Xml:Serialization:UseLegacySerializerGeneration" value="true" />
</appSettings>
</configuration>
Como alternativa à configuração acima, você pode usar a seguinte configuração em um computador executando o .NET Framework 4.5 ou versão posterior:
<configuration>
<system.xml.serialization>
<xmlSerializer useLegacySerializerGeneration="true"/>
</system.xml.serialization>
</configuration>
Observação
O comutador <xmlSerializer useLegacySerializerGeneration="true"/> só funciona em um computador que executa .NET Framework versão 4.5 ou posterior. A abordagem acima appSettings funciona em todas as versões do .NET Framework.