Jak zapobiec pustym atrybutom xmlns w danych wyjściowych z XmlDocument platformy .NET?

118

Podczas generowania XML z XmlDocument w .NET, pusty xmlnsatrybut pojawia się po pierwszym wstawieniu elementu bez skojarzonej przestrzeni nazw; jak można temu zapobiec?

Przykład:

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

Wynik:

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

Pożądane wyjście:

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

Czy istnieje rozwiązanie, które można zastosować do XmlDocumentkodu, a nie coś, co dzieje się po konwersji dokumentu na ciąg OuterXml?

Powodem, dla którego to robię, jest sprawdzenie, czy mogę dopasować standardowy kod XML konkretnego protokołu za pomocą XML wygenerowanego przez XmlDocument. Pusty xmlnsatrybut nie może zepsuć ani zmylić parsera, ale nie jest również obecny w żadnym zastosowaniu tego protokołu, jakie widziałem.

Neil C. Obremski
źródło

Odpowiedzi:

111

Dzięki odpowiedzi Jeremy'ego Lwa i trochę więcej zabawy, wymyśliłem, jak usunąć puste xmlnsatrybuty: podaj przestrzeń nazw węzła głównego podczas tworzenia dowolnego węzła podrzędnego, na którym nie chcesz mieć prefiksu. Używanie przestrzeni nazw bez przedrostka w katalogu głównym oznacza, że ​​musisz użyć tej samej przestrzeni nazw na elementach podrzędnych, aby również nie miały przedrostków.

Naprawiono kod:

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

Dziękuję wszystkim za wszystkie odpowiedzi, które poprowadziły mnie we właściwym kierunku!

Neil C. Obremski
źródło
1
Dokładnie. Umieszczenie elementu <loner> w przestrzeni nazw „cokolwiek: przestrzeń nazw-1.0” oznacza, że ​​pusty atrybut xmlns (który nie umieszcza go w przestrzeni nazw) nie zostanie do niego dodany podczas serializacji. Jeśli potrzebujesz odświeżenia informacji o działaniu przestrzeni nazw, zajrzyj na stronę jclark.com/xml/xmlns.htm
JeniT
2
Uważaj: elementy tego potrzebują (a może lepiej doc.DocumentElement.NamespaceURI), ale nie określaj przestrzeni nazw, dla CreateAttribute()której otrzymasz, xmlns:psomethingnawet jeśli jest to ten sam uri.
Jason Kleban,
87

To jest wariant odpowiedzi JeniT (przy okazji bardzo dziękuję!)

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

Eliminuje to konieczność kopiowania lub powtarzania przestrzeni nazw wszędzie.

C Johnson
źródło
3
Najlepsza odpowiedź według mnie. Nie musimy wiedzieć, jaka jest domyślna przestrzeń nazw dokumentu (przydatne, gdy nie tworzymy pliku xml od zera, np. W scenariuszach odczytu i modyfikacji).
MuiBienCarlota
11

Jeśli <loner>element w przykładowym kodzie XML nie miałby xmlnsdomyślnej deklaracji przestrzeni nazw, to znajdowałby się w whatever:name-space-1.0przestrzeni nazw, a nie w żadnej. Jeśli tego chcesz, musisz utworzyć element w tej przestrzeni nazw:

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

Jeśli chcesz, aby <loner>element nie znajdował się w przestrzeni nazw, to utworzony XML jest dokładnie tym, czego potrzebujesz i nie powinieneś się martwić o xmlnsatrybut, który został dodany automatycznie.

JeniT
źródło
3
Problem leży w niezgodnych parserach XML (zwykle od firmy Microsoft), które nie radzą sobie z xmnls = "").
Craig Trader,
2
/. nazywa. Chcą z powrotem swój losowy komentarz do MS.
@W. Craig Trader - nie mogę powiedzieć, że napotkałem to jako problem. Przykład?
Kev,
1
Dobrze, nie chcę, aby węzeł <loner /> miał przestrzeń nazw, ale nie chcę, aby miał też pusty atrybut przestrzeni nazw (xmlns). Moje rozumowanie jest takie, aby sprawdzić, czy mogę dopasować wyjście XML określonego protokołu, który jest tak skonfigurowany.
Neil C. Obremski,
5
To nie było przypadkowe uderzenie. Blok aplikacji Microsoft Updater używa manifestu XML, aby określić, co należy dostarczyć klientowi. Niestety, parser manifestu nie radzi sobie z xmlns = ""; Musiałem napisać postprocesor, który usuwałby puste atrybuty xmlns.
Craig Trader,
7

Ponieważ root znajduje się w przestrzeni nazw bez prefiksu, każde dziecko roota, które chce zostać pozbawione przestrzeni nazw, musi zostać wyprowadzone jak w przykładzie. Rozwiązaniem byłoby dodanie przedrostka elementu głównego w następujący sposób:

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

kod:

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);
jlew
źródło
Dzięki, ale dodanie przestrzeni nazw do rzeczywistego katalogu głównego zepsułoby mój XML w odniesieniu do konkretnego protokołu, z którym pracuję.
Neil C. Obremski,
Ach! Zrozumiałem bardziej, co mówisz, i wziąłem to pod uwagę, pisząc własną odpowiedź. Dzięki Jeremy
Neil C. Obremski
0

Jeśli to możliwe, utwórz klasę serializacji, a następnie wykonaj:

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

Jest to bezpieczniejsze i możesz kontrolować przestrzenie nazw za pomocą atrybutów, jeśli naprawdę potrzebujesz większej kontroli.

ilitirit
źródło
0

Rozwiązałem problem, używając wzorca fabrycznego. Stworzyłem fabrykę obiektów XElement. Jako parametr do tworzenia instancji fabryki określiłem obiekt XNamespace. Tak więc za każdym razem, gdy XElement jest tworzony przez fabrykę, przestrzeń nazw zostanie dodana automatycznie. Oto kod fabryki:

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);
    }
}
brinke
źródło
1
OP pytał XmlDocument, nie XDocument.
John Saunders
0

Tak, możesz zapobiec XMLNS z XmlElement. Pierwszy czas tworzenia nadchodzi: właśnie tak

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

Zmień kod: i przekaż przestrzeń nazw xml w ten sposób

Kod C #:

XmlElement bookElement = xdoc.CreateElement("trkpt", "http://www.topografix.com/GPX/1/1");
bookElement.SetAttribute("lat", "30.53597");
bookElement.SetAttribute("lon", "97.753324");
Debabrata Ghosh
źródło