Nota
O acesso a esta página requer autorização. Podes tentar iniciar sessão ou mudar de diretório.
O acesso a esta página requer autorização. Podes tentar mudar de diretório.
O Windows Communication Foundation (WCF) pode usar duas tecnologias de serialização diferentes para transformar os dados em seu aplicativo em XML que é transmitido entre clientes e serviços: DataContractSerializer e XmlSerializer.
DataContractSerializer (Serializador de Contratos de Dados)
Por padrão, o WCF usa a DataContractSerializer classe para serializar tipos de dados. Este serializador suporta os 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 IXmlSerializable interface.
Muitos tipos de coleção comuns, incluindo vários tipos de coleção genéricos.
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 em conjunto com tipos de contrato de dados, é a maneira recomendada de escrever novos serviços WCF. Para obter mais informações, consulte Usando contratos de dados.
XmlSerializer
WCF também suporta a XmlSerializer classe. A XmlSerializer classe não é exclusiva do WCF. É o mesmo mecanismo de serialização que ASP.NET serviços da Web usam. A XmlSerializer classe suporta um conjunto muito mais restrito de tipos do que a DataContractSerializer classe, mas permite muito mais controle sobre o XML resultante e suporta muito mais do padrão XSD (XML Schema Definition Language). Ele também não requer nenhum atributo declarativo 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 suporta tipos de contrato de dados.
Ao usar Svcutil.exe ou o recurso Adicionar referência de serviço no Visual Studio para gerar código de 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.
Alternar para o XmlSerializer
Às vezes, você pode ter que alternar manualmente para o XmlSerializer. Isso acontece, por exemplo, nos seguintes casos:
Ao migrar um aplicativo de ASP.NET Web Services para WCF, convém reutilizar tipos existentes XmlSerializercompatíveis em vez de criar novos tipos de contrato de dados.
Quando o controle preciso sobre o XML que aparece nas mensagens é importante, mas um documento WSDL (Web Services Description Language) 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 é 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 seu 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ê acidentalmente usar o serializador errado, você pode estar divulgando informações do tipo que você não pretendia divulgar.
Por exemplo, a DataContractSerializer classe serializa apenas 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 num contrato de serviço onde a classe XmlSerializer é selecionada, o membro creditCardNumber é serializado, o que provavelmente não era pretendido.
Embora 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 ligação diferente ou alterando outras definições de configuração.
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 divulgação. Esta recomendação aplica-se tanto quando é realizada uma mudança manual para o XmlSerializer como quando é realizada uma comutação automática (por Svcutil.exe, Adicionar Referência de Serviço, ou uma ferramenta semelhante). Isso ocorre porque o motor de serialização XmlSerializer suporta o carregamento de assemblies de serialização pré-gerados, desde que sejam assinados com a mesma chave que a aplicação. Uma aplicação não assinada está completamente desprotegida contra a possibilidade de um assembly malicioso, que corresponda ao nome esperado do assembly de serialização pré-gerado, ser colocado na pasta da aplicação ou na cache global de assemblies. É claro que um atacante deve primeiro ter acesso de escrita 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 XmlSerializer mecanismo de serialização cria e usa conjuntos de serialização temporários nesta pasta. Você deve estar ciente de que qualquer processo com acesso de gravação à pasta temporária pode sobrescrever esses conjuntos de serialização por código malicioso.
Regras para suporte a XmlSerializer
Não é possível aplicar atributos XmlSerializer compatíveis diretamente a os parâmetros de operação de contrato ou os valores de retorno. No entanto, eles podem ser aplicados 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 digitadas, esses atributos substituem propriedades que entram em conflito nos atributos de mensagem digitada. 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 quando se utiliza o XmlSerializer.
Observação
Nesse caso, o XmlSerializer lança a seguinte exceção, que é liberada 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 tal exceção, investigue se essa situação se aplica.
O WCF não suporta os SoapIncludeAttribute atributos e XmlIncludeAttribute em contratos de mensagem e contratos de operação, use o KnownTypeAttribute atributo em vez disso.
Tipos que implementam a interface IXmlSerializable
Os tipos que implementam a interface IXmlSerializable são totalmente suportados pelo DataContractSerializer. O XmlSchemaProviderAttribute atributo deve sempre ser aplicado a esses tipos para controlar seu esquema.
Advertência
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 em seu valor padrão defalse. Este é o uso mais comum deIXmlSerializabletipos.Os tipos de elemento são usados quando um tipo
IXmlSerializabledeve controlar o seu próprio nome do 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 elemento – você pode especificarnullem vez do nome do método noXmlSchemaProviderAttribute. No entanto, seIsAnyétruee um método de provedor de esquema é especificado, o método deve retornarnull.Tipos herdados DataSet são
IXmlSerializabletipos que não são marcados com oXmlSchemaProviderAttributeatributo. Em vez disso, eles dependem do método GetSchema para geração de esquema. Esse padrão é usado para oDataSettipo e seu conjunto de dados tipado deriva uma classe em versões anteriores do .NET Framework, mas agora está obsoleto e é suportado apenas por motivos herdados. Não confie neste padrão e aplique sempre oXmlSchemaProviderAttributeaos seusIXmlSerializabletipos.
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 escreve o elemento de invólucro para o membro de dados e transfere o controle para o método WriteXml. A WriteXml implementação pode escrever qualquer XML, o que inclui a adição de atributos ao elemento wrapper. Depois de WriteXml estar 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 de XML no elemento envoltório para o membro de dados e passa o controle para o método ReadXml. O método deve ler o elemento inteiro, incluindo as tags start e end. Certifique-se de que seu ReadXml código lida com o caso em que o elemento está vazio. Além disso, sua ReadXml implementação não deve depender do elemento wrapper ser 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. Finalmente, é possível utilizar tipos IXmlSerializable com a preservação do gráfico de objetos ativada e com o NetDataContractSerializer. Todos esses recursos exigem que o serializador WCF anexe certos atributos ao elemento wrapper ("nil" e "type" no namespace XML Schema Instance e "Id", "Ref", "Type" e "Assembly" em um namespace específico do WCF).
Atributos a serem ignorados ao implementar o ReadXml
Antes de passar o controle para o ReadXml código, o desserializador examina o elemento XML, deteta esses atributos XML especiais e age sobre eles. Por exemplo, se "nil" é true, um valor nulo é desserializado e ReadXml não é chamado. Se o polimorfismo for detetado, o conteúdo do elemento será desserializado como se fosse um tipo diferente. A implementação do ReadXml tipo atribuído polimorficamente é chamada. Em qualquer caso, a implementação ReadXml deve ignorar esses atributos especiais, uma vez que são manipulados pelo desserializador.
Considerações de esquema para tipos de conteúdo IXmlSerializable
Ao exportar o esquema e um tipo de conteúdo IXmlSerializable, o método do provedor de esquema é chamado. Um XmlSchemaSet é passado para o método do fornecedor 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 do esquema. Quando o método do provedor de esquema deve adicionar um item ao conjunto de esquemas, ele deve determinar se um XmlSchema com o namespace apropriado já existe no conjunto. Se isso acontecer, o método do provedor de esquema deve adicionar o novo item ao arquivo 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 IXmlSerializable tipo que é exportado como tipo "A" no namespace "B", é possível que, no momento em que o método do provedor de esquema é chamado, o conjunto de esquemas já contenha o esquema para "B" para conter 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 determinado IXmlSerializable tipo. Esse nome qualificado também serve como o nome do contrato de dados e namespace 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 método Export é chamado para todos os tipos relevantes em XsdDataContractExporter e a propriedade Schemas é acessada), o tipo exista no conjunto de esquemas. O acesso à propriedade Schemas antes de todas as chamadas relevantes Export terem sido 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 para o tipo IXmlSerializable é exportado como um tipo anónimo sempre que o tipo IXmlSerializable é usado como membro de dados. O IXmlSerializable tipo ainda tem um nome de contrato de dados e 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. Este caso equivale a devolver 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 do 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 do esquema. Se o tipo tiver o XmlRootAttribute atributo aplicado a ele, a declaração de elemento global será exportada usando o seguinte: ElementName, Namespace e IsNullable propriedades. Os padrões com XmlRootAttribute aplicado 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 a tipos de conjuntos de dados herdados. Observe que o XmlRootAttribute não pode substituir as declarações de elemento global adicionadas através de código personalizado ao XmlSchemaSet, seja usando o método do provedor de esquema ou através do GetSchema para tipos de conjuntos de dados herdados.
Tipos de elementos IXmlSerializable
IXmlSerializable Os tipos de elemento têm a IsAny propriedade definida como true ou têm seu método de provedor de esquema retornado null.
Serializar e desserializar um tipo de elemento é muito semelhante a serializar e desserializar um tipo de conteúdo. No entanto, existem algumas diferenças importantes:
Espera-se que a
WriteXmlimplementação escreva exatamente um elemento (que pode, é claro, conter vários elementos filho). Não deve escrever atributos fora deste ú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 leia o único elemento queWriteXmlproduz.Ao serializar um tipo de elemento regularmente (por exemplo, como um membro de dados em um contrato de dados), o serializador produz um elemento wrapper antes de chamar
WriteXml, como acontece com os 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 em torno do elemento que escreve, a menos que um nome de raiz e namespace sejam explicitamente especificados ao construir o serializador nos construtoresWriteXmlouDataContractSerializer. 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 da raiz e o namespace no momento da 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 gráfico de objeto não pode ser ativada e oNetDataContractSerializernão pode ser usado.Ao desserializar um tipo de elemento no nível superior sem especificar o nome da raiz e o namespace no momento da construção, IsStartObject retorna
truese puder encontrar o início de qualquer elemento. ReadObject com o parâmetroverifyObjectNamedefinido paratruecomporta-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, tal como descrito numa secção anterior, exceto que o método do fornecedor de esquema pode adicionar qualquer esquema adicional ao XmlSchemaSet, tal como acontece com tipos de conteúdo. O uso do atributo com tipos de XmlRootAttribute elemento não é permitido, e declarações de elemento global nunca são emitidas para esses tipos.
Diferenças do XmlSerializer
A interface IXmlSerializable e os atributos XmlSchemaProviderAttribute e XmlRootAttribute também são compreendidos pelo XmlSerializer. No entanto, existem algumas diferenças na forma como estes são tratados no modelo de contrato de dados. As diferenças importantes estão resumidas na seguinte lista:
O método do 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
IsAnyestátrueno modelo de contrato de dados, mas não com oXmlSerializer.Quando o atributo
XmlRootAttributenão está presente para tipos de conteúdo ou para conjuntos de dados legados, oXmlSerializerexporta 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.
Esteja ciente dessas diferenças ao criar tipos que são usados com ambas as tecnologias de serialização.
Importando esquema IXmlSerializable
Ao importar um esquema gerado a partir de IXmlSerializable tipos, existem algumas possibilidades:
O esquema gerado pode ser um esquema de contrato de dados válido, conforme descrito em 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 esquema que envolve atributos XML que não são suportados no modelo de contrato de dados. Nesse caso, você pode importar o esquema como
IXmlSerializabletipos. Este modo de importação não está ativado por padrão, mas pode ser facilmente ativado – por exemplo, com a opção de linha de comando/importXmlTypespara a ServiceModel Metadata Utility Tool (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 ofereça suporte a uma gama mais ampla de esquema – consulte o tópico sobre como usar oXmlSerializer.Você pode querer reutilizar seus tipos existentes
IXmlSerializableno proxy em vez de gerar novos. Nesse caso, o recurso de tipos referenciados descrito no tópico Importando esquema para gerar tipos pode ser usado para indicar o tipo a ser reutilizado. Isso corresponde ao uso do interruptor no svcutil.exe, que especifica o conjunto/compilação que contém os tipos a serem reutilizados.
Comportamento herdado do XmlSerializer
No .NET Framework 4.0 e anteriores, o XmlSerializer gerou assemblies de serialização temporários gravando código C# em um arquivo. O ficheiro foi então compilado numa montagem. Esse comportamento teve algumas consequências indesejáveis, como diminuir o tempo de inicialização para o 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 tiveres problemas de compatibilidade, como a falha ao serializar uma classe derivada XmlSerializer com um novo sobrescritor não público, podes voltar para o XMLSerializer comportamento legado 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 uma máquina executando o .NET Framework 4.5 ou versão posterior:
<configuration>
<system.xml.serialization>
<xmlSerializer useLegacySerializerGeneration="true"/>
</system.xml.serialization>
</configuration>
Observação
O <xmlSerializer useLegacySerializerGeneration="true"/> switch só funciona em uma máquina que executa o .NET Framework 4.5 ou versão posterior. A abordagem acima appSettings funciona em todas as versões do .NET Framework.