Como Dave pediu que eu repetisse minha resposta para omitir todos os namespaces xsi e xsd ao serializar um objeto no .NET , atualizei esta postagem e repeti minha resposta aqui no link mencionado acima. O exemplo usado nesta resposta é o mesmo exemplo usado para a outra pergunta. O que se segue é copiado, literalmente.
Depois de ler a documentação da Microsoft e várias soluções online, descobri a solução para esse problema. Ele funciona com a XmlSerializer
serialização XML interna e personalizada via IXmlSerialiazble
.
Para saber mais, usarei a mesma MyTypeWithNamespaces
amostra XML usada nas respostas até agora.
[XmlRoot("MyTypeWithNamespaces", Namespace="urn:Abracadabra", IsNullable=false)]
public class MyTypeWithNamespaces
{
// As noted below, per Microsoft's documentation, if the class exposes a public
// member of type XmlSerializerNamespaces decorated with the
// XmlNamespacesDeclarationAttribute, then the XmlSerializer will utilize those
// namespaces during serialization.
public MyTypeWithNamespaces( )
{
this._namespaces = new XmlSerializerNamespaces(new XmlQualifiedName[] {
// Don't do this!! Microsoft's documentation explicitly says it's not supported.
// It doesn't throw any exceptions, but in my testing, it didn't always work.
// new XmlQualifiedName(string.Empty, string.Empty), // And don't do this:
// new XmlQualifiedName("", "")
// DO THIS:
new XmlQualifiedName(string.Empty, "urn:Abracadabra") // Default Namespace
// Add any other namespaces, with prefixes, here.
});
}
// If you have other constructors, make sure to call the default constructor.
public MyTypeWithNamespaces(string label, int epoch) : this( )
{
this._label = label;
this._epoch = epoch;
}
// An element with a declared namespace different than the namespace
// of the enclosing type.
[XmlElement(Namespace="urn:Whoohoo")]
public string Label
{
get { return this._label; }
set { this._label = value; }
}
private string _label;
// An element whose tag will be the same name as the property name.
// Also, this element will inherit the namespace of the enclosing type.
public int Epoch
{
get { return this._epoch; }
set { this._epoch = value; }
}
private int _epoch;
// Per Microsoft's documentation, you can add some public member that
// returns a XmlSerializerNamespaces object. They use a public field,
// but that's sloppy. So I'll use a private backed-field with a public
// getter property. Also, per the documentation, for this to work with
// the XmlSerializer, decorate it with the XmlNamespaceDeclarations
// attribute.
[XmlNamespaceDeclarations]
public XmlSerializerNamespaces Namespaces
{
get { return this._namespaces; }
}
private XmlSerializerNamespaces _namespaces;
}
Isso é tudo nessa classe. Agora, alguns se opuseram a ter um XmlSerializerNamespaces
objeto em algum lugar dentro de suas classes; mas como você pode ver, coloquei-o ordenadamente no construtor padrão e expus uma propriedade pública para retornar os namespaces.
Agora, quando chegar a hora de serializar a classe, você usaria o seguinte código:
MyTypeWithNamespaces myType = new MyTypeWithNamespaces("myLabel", 42);
/******
OK, I just figured I could do this to make the code shorter, so I commented out the
below and replaced it with what follows:
// You have to use this constructor in order for the root element to have the right namespaces.
// If you need to do custom serialization of inner objects, you can use a shortened constructor.
XmlSerializer xs = new XmlSerializer(typeof(MyTypeWithNamespaces), new XmlAttributeOverrides(),
new Type[]{}, new XmlRootAttribute("MyTypeWithNamespaces"), "urn:Abracadabra");
******/
XmlSerializer xs = new XmlSerializer(typeof(MyTypeWithNamespaces),
new XmlRootAttribute("MyTypeWithNamespaces") { Namespace="urn:Abracadabra" });
// I'll use a MemoryStream as my backing store.
MemoryStream ms = new MemoryStream();
// This is extra! If you want to change the settings for the XmlSerializer, you have to create
// a separate XmlWriterSettings object and use the XmlTextWriter.Create(...) factory method.
// So, in this case, I want to omit the XML declaration.
XmlWriterSettings xws = new XmlWriterSettings();
xws.OmitXmlDeclaration = true;
xws.Encoding = Encoding.UTF8; // This is probably the default
// You could use the XmlWriterSetting to set indenting and new line options, but the
// XmlTextWriter class has a much easier method to accomplish that.
// The factory method returns a XmlWriter, not a XmlTextWriter, so cast it.
XmlTextWriter xtw = (XmlTextWriter)XmlTextWriter.Create(ms, xws);
// Then we can set our indenting options (this is, of course, optional).
xtw.Formatting = Formatting.Indented;
// Now serialize our object.
xs.Serialize(xtw, myType, myType.Namespaces);
Depois de fazer isso, você deverá obter a seguinte saída:
<MyTypeWithNamespaces>
<Label xmlns="urn:Whoohoo">myLabel</Label>
<Epoch>42</Epoch>
</MyTypeWithNamespaces>
Usei esse método com êxito em um projeto recente com uma hierarquia profunda de classes serializadas em XML para chamadas de serviço da web. A documentação da Microsoft não é muito clara sobre o que fazer com o XmlSerializerNamespaces
membro publicamente acessível depois que você a criou, e muitos pensam que é inútil. Mas, seguindo a documentação e usando-a da maneira mostrada acima, você pode personalizar como o XmlSerializer gera XML para suas classes sem recorrer a um comportamento não suportado ou "serializar sua própria" serialização implementando IXmlSerializable
.
Espero que esta resposta descanse, de uma vez por todas, como se livrar do padrão xsi
e dos xsd
namespaces gerados pelo XmlSerializer
.
ATUALIZAÇÃO: Eu só quero ter certeza de que respondi à pergunta do OP sobre como remover todos os espaços para nome. Meu código acima funcionará para isso; Deixa-me mostrar-te como. Agora, no exemplo acima, você realmente não pode se livrar de todos os namespaces (porque existem dois namespaces em uso). Em algum lugar do seu documento XML, você precisará ter algo parecido xmlns="urn:Abracadabra" xmlns:w="urn:Whoohoo
. Se a classe no exemplo fizer parte de um documento maior, em algum lugar acima de um espaço para nome deverá ser declarado para um (ou ambos) Abracadbra
e Whoohoo
. Caso contrário, o elemento em um ou em ambos os espaços para nome deve ser decorado com um prefixo de algum tipo (você não pode ter dois espaços para nome padrão, certo?). Portanto, para este exemplo, Abracadabra
é o espaço para nome padrão. Eu poderia dentro da minha MyTypeWithNamespaces
classe adicionar um prefixo de namespace para o Whoohoo
namespace da seguinte forma:
public MyTypeWithNamespaces
{
this._namespaces = new XmlSerializerNamespaces(new XmlQualifiedName[] {
new XmlQualifiedName(string.Empty, "urn:Abracadabra"), // Default Namespace
new XmlQualifiedName("w", "urn:Whoohoo")
});
}
Agora, na minha definição de classe, indiquei que o <Label/>
elemento está no espaço "urn:Whoohoo"
para nome , então não preciso fazer mais nada. Quando agora serializo a classe usando meu código de serialização acima inalterado, esta é a saída:
<MyTypeWithNamespaces xmlns:w="urn:Whoohoo">
<w:Label>myLabel</w:Label>
<Epoch>42</Epoch>
</MyTypeWithNamespaces>
Como <Label>
está em um espaço para nome diferente do restante do documento, ele deve, de alguma forma, ser "decorado" com um espaço para nome. Observe que ainda não existem espaços para nome xsi
e xsd
.
Isso encerra minha resposta para a outra pergunta. Mas eu queria ter certeza de responder à pergunta do OP sobre não usar namespaces, pois acho que ainda não a resolvi. Suponha que faça <Label>
parte do mesmo espaço para nome que o restante do documento, neste caso urn:Abracadabra
:
<MyTypeWithNamespaces>
<Label>myLabel<Label>
<Epoch>42</Epoch>
</MyTypeWithNamespaces>
Seu construtor ficaria como no meu primeiro exemplo de código, junto com a propriedade pública para recuperar o espaço para nome padrão:
// As noted below, per Microsoft's documentation, if the class exposes a public
// member of type XmlSerializerNamespaces decorated with the
// XmlNamespacesDeclarationAttribute, then the XmlSerializer will utilize those
// namespaces during serialization.
public MyTypeWithNamespaces( )
{
this._namespaces = new XmlSerializerNamespaces(new XmlQualifiedName[] {
new XmlQualifiedName(string.Empty, "urn:Abracadabra") // Default Namespace
});
}
[XmlNamespaceDeclarations]
public XmlSerializerNamespaces Namespaces
{
get { return this._namespaces; }
}
private XmlSerializerNamespaces _namespaces;
Então, mais tarde, no seu código que usa o MyTypeWithNamespaces
objeto para serializá-lo, você o chamaria como eu fiz acima:
MyTypeWithNamespaces myType = new MyTypeWithNamespaces("myLabel", 42);
XmlSerializer xs = new XmlSerializer(typeof(MyTypeWithNamespaces),
new XmlRootAttribute("MyTypeWithNamespaces") { Namespace="urn:Abracadabra" });
...
// Above, you'd setup your XmlTextWriter.
// Now serialize our object.
xs.Serialize(xtw, myType, myType.Namespaces);
E XmlSerializer
isso cuspiria de volta o mesmo XML, como mostrado imediatamente acima, sem espaços para nome adicionais na saída:
<MyTypeWithNamespaces>
<Label>myLabel<Label>
<Epoch>42</Epoch>
</MyTypeWithNamespaces>