Como evitar atributos xmlns em branco na saída do XmlDocument do .NET?


118

Ao gerar XML a partir de XmlDocument em .NET, um xmlnsatributo em branco aparece na primeira vez que um elemento sem um namespace associado é inserido; Como isto pode ser evitado?

Exemplo:

XmlDocument xml = new XmlDocument();
xml.AppendChild(xml.CreateElement("root",
    "whatever:name-space-1.0"));
xml.DocumentElement.AppendChild(xml.CreateElement("loner"));
Console.WriteLine(xml.OuterXml);

Resultado:

<root xmlns="whatever:name-space-1.0"><loner xmlns="" /></root>

Resultado desejado :

<root xmlns="whatever:name-space-1.0"><loner /></root>

Existe uma solução aplicável ao XmlDocumentcódigo, não algo que ocorre após a conversão do documento em uma string com OuterXml?

Meu raciocínio para fazer isso é ver se posso corresponder ao XML padrão de um protocolo específico usando XML gerado por XmlDocument. O xmlnsatributo em branco pode não quebrar ou confundir um analisador, mas também não está presente em nenhum uso que eu tenha visto desse protocolo.

Respostas:


111

Graças à resposta de Jeremy Lew e um pouco mais de brincadeira, descobri como remover xmlnsatributos em branco : passar o namespace do nó raiz ao criar qualquer nó filho que você não deseja que tenha um prefixo. Usar um namespace sem um prefixo na raiz significa que você precisa usar esse mesmo namespace em elementos filho para que eles também não tenham prefixos.

Código Fixo:

XmlDocument xml = new XmlDocument();
xml.AppendChild(xml.CreateElement("root", "whatever:name-space-1.0"));
xml.DocumentElement.AppendChild(xml.CreateElement("loner", "whatever:name-space-1.0")); 
Console.WriteLine(xml.OuterXml);

Obrigado a todos por todas as respostas que me levaram na direção certa!


1
Precisamente. Colocar o elemento <loner> no namespace "qualquer: name-space-1.0" significa que o atributo xmlns vazio (que o coloca em nenhum namespace) não será adicionado a ele quando for serializado. Se você precisar se atualizar sobre como funcionam os namespaces, dê uma olhada em jclark.com/xml/xmlns.htm
JeniT

2
Cuidado: os elementos precisam disso (ou talvez melhor doc.DocumentElement.NamespaceURI), mas não especifique um namespace para CreateAttribute()o xmlns:psomethingmesmo que seja o mesmo uri.
Jason Kleban

87

Esta é uma variante da resposta de JeniT (muito, muito obrigado!)

XmlElement new_element = doc.CreateElement("Foo", doc.DocumentElement.NamespaceURI);

Isso elimina a necessidade de copiar ou repetir o namespace em todos os lugares.


3
Melhor resposta de acordo com mim. Não precisamos saber o que é o namespace padrão do documento (útil quando não criamos um arquivo xml do zero, ou seja, em cenários de leitura e modificação).
MuiBienCarlota

11

Se o <loner>elemento em seu XML de amostra não tivesse a xmlnsdeclaração de namespace padrão nele, ele estaria no whatever:name-space-1.0namespace em vez de estar em nenhum namespace. Se é isso que você deseja, você precisa criar o elemento nesse namespace:

xml.CreateElement("loner", "whatever:name-space-1.0")

Se você quiser que o <loner>elemento não esteja em nenhum namespace, o XML que foi produzido é exatamente o que você precisa e você não deve se preocupar com o xmlnsatributo que foi adicionado automaticamente para você.


3
O problema reside em analisadores XML incompatíveis (normalmente da Microsoft) que não conseguem lidar com xmnls = "").
Craig Trader,

2
/. chamado. Eles querem seus comentários aleatórios de contusão de MS de volta.

@W. Craig Trader - não posso dizer que achei isso um problema. Exemplo?
Kev

1
Correto, não quero que o nó <loner /> tenha um namespace, mas também não quero que ele tenha um atributo de namespace em branco (xmlns). Meu raciocínio é apenas para ver se posso corresponder à saída XML de um protocolo específico configurado dessa forma.
Neil C. Obremski,

5
Não foi um golpe aleatório. O bloco de aplicativos do Microsoft Updater usa um manifesto XML para determinar o que entregar a um cliente. Infelizmente, o analisador de manifesto não pode lidar com xmlns = ""; Tive que escrever um pós-processador que retiraria os atributos xmlns vazios.
Craig Trader,

7

Uma vez que root está em um namespace sem prefixo, qualquer filho de root que deseja remover o namespace deve ter a saída como seu exemplo. A solução seria prefixar o elemento raiz da seguinte forma:

<w:root xmlns:w="whatever:name-space-1.0">
   <loner/>
</w:root>

código:

XmlDocument doc = new XmlDocument();
XmlElement root = doc.CreateElement( "w", "root", "whatever:name-space-1.0" );
doc.AppendChild( root );
root.AppendChild( doc.CreateElement( "loner" ) );
Console.WriteLine(doc.OuterXml);

Obrigado, mas adicionar o namespace à raiz real quebraria meu XML em relação ao protocolo específico com o qual estou trabalhando.
Neil C. Obremski

Ah! Percebi melhor o que você estava dizendo e levei isso em consideração ao escrever minha própria resposta. Obrigado Jeremy
Neil C. Obremski

0

Se possível, crie uma classe de serialização e faça:

XmlSerializerNamespaces ns = new XmlSerializerNamespaces();
ns.Add("", "");
XmlSerializer serializer = new XmlSerializer(yourType);
serializer.Serialize(xmlTextWriter, someObject, ns);

É mais seguro e você pode controlar os namespaces com atributos se realmente precisar de mais controle.


0

Resolvi o problema usando o padrão de fábrica. Criei uma fábrica para objetos XElement. Como parâmetro para a instanciação da fábrica, especifiquei um objeto XNamespace. Portanto, toda vez que um XElement é criado pela fábrica, o namespace será adicionado automaticamente. Aqui está o código da fábrica:

internal class XElementFactory
{
    private readonly XNamespace currentNs;

    public XElementFactory(XNamespace ns)
    {
        this.currentNs = ns;
    }

    internal XElement CreateXElement(String name, params object[] content)
    {
        return new XElement(currentNs + name, content);
    }
}

1
O OP estava perguntando sobre XmlDocument, não XDocument.
John Saunders

0

Sim, você pode impedir o XMLNS do XmlElement. A hora da primeira criação está chegando: assim

<trkpt lat="30.53597" lon="-97.753324" xmlns="">
    <ele>249.118774</ele>
    <time>2006-05-05T14:34:44Z</time>
</trkpt>

Altere o código: e passe o namespace xml assim

Código C #:

XmlElement bookElement = xdoc.CreateElement("trkpt", "http://www.topografix.com/GPX/1/1");
bookElement.SetAttribute("lat", "30.53597");
bookElement.SetAttribute("lon", "97.753324");
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.