[C#] Kan een node in een X(ml)Document niet vinden

Pagina: 1
Acties:

Onderwerpen


Acties:
  • 0 Henk 'm!

  • _Thanatos_
  • Registratie: Januari 2001
  • Laatst online: 05-09 14:39

_Thanatos_

Ja, en kaal

Topicstarter
Weer gezever met namespaces vrees ik, maar ik heb er helemaal geen behoefte aan om ze hardcoded te gaan definieren als ze in de xml keurignetjes staan. Dit is een stukje van de xml die ik probeer in te lezen:
XML:
1
2
3
4
5
6
<mediawiki xmlns="http://www.mediawiki.org/xml/export-0.3/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.mediawiki.org/xml/export-0.3/ http://www.mediawiki.org/xml/export-0.3.xsd" version="0.3" xml:lang="en">
  <page>
    <title>Main Page</title>
    <id>6</id>
  </page>
</mediawiki>

En mijn C# code:
C#:
1
2
3
XmlReader reader = XmlReader.Create(stream, new XmlReaderSettings() { ValidationType = ValidationType.None });
XDocument doc = XDocument.Load(reader);
var title = doc.Descendants("title");

Veel simpeler kan niet. Ik heb geen behoefte aan uitgebreide validatie; ik hoef alleen maar de nodes te hebben. Kan mij het schelen als er iets tussen zit wat er niet hoort. Ook houdt ik me in m'n code ook niet bezig met namespaces, want ik heb daar helemaal geen controle over, gezien het van een mediawiki-site komt. Lekker simpel, en wat het moet doen is (met een beetje kennis van linq2xml) kraakhelder wat het moet doen.

Toch werkt het niet, want hij kan dat title element niet vinden. Waarom? Geen flauw idee, maar ik lees overal dat je je namespaces hardcoded in je code moet plempen en bij IEDERE node-selectie (althans dat is het geval bij XmlDocument en SelectNodes e.d.) hem eraan moet herinneren welke namespaces en prefixen je ook al weer had gewild. Dat wordt in dit topic beschreven.

Maar mijn XML document heeft gewoon een default namespace, dus prefixen zijn helemaal niet nodig. Daarnaast zou het mijn code onnodig bloaten, zou het uit elkaar klappen als mediawiki besluit 1 letter in de namespace te veranderen (of em weg te halen) en het maakt selecties onnodig complexer en moeilijker leesbaar.

Dus, vond ik een stukje code om alle namespaces uit een document te strippen, maar dat werkt alleen met linq2xml:
C#:
1
2
3
4
5
6
7
8
public static XElement StripNS(XElement root) {
    return new XElement(
         root.Name.LocalName,
         root.HasElements ?
              root.Elements().Select(el => StripNS(el)) :
              (object) root.Value
    );
}

En nu kan ik m'n document als volgt inladen:
C#:
1
XElement doc = StripNS(XDocument.Load(reader).Elements().First());

Valt nog mee dus.

Wat vinden jullie? Zijn er mooiere methodes (vast wel :P) of maak ik een grondige denkfout (vast wel :P) of heeft iemand hier al een keer een handige workaround voor gevonden die geen linq2xml vereist (want ik gebruik denk ik toch liever XPath)?

日本!🎌


Acties:
  • 0 Henk 'm!

  • pedorus
  • Registratie: Januari 2008
  • Niet online
Als je XPath wil gebruiken, waarom kijk je dan naar linq? :p Iets als eerste google-hit op c# xpath zal vast werken, en er hoeft helemaal niks met XML-namespaces te gebeuren. Als dit overigens om een complete wiki in een bestand gaat, kun je trouwens beter kijken naar XmlReader (direct) ivm geheugengebruik.

Vitamine D tekorten in Nederland | Dodelijk coronaforum gesloten


Acties:
  • 0 Henk 'm!

  • CMG
  • Registratie: Februari 2002
  • Laatst online: 10-12-2024

CMG

Kijk eens naar de XSD.exe tool.

Daarmee kan je van je XML file een .XSD genereren en vanuit de .XSD een .cs of .vb class. Deze is dan zo ingericht dat je met XMLSerializer je XML file naar een managed object kunt deserializen, je kan je doc dan gewoon refereren aan mediawiki.Page.Title om je title op te halen bijvoorbeeld.

Als je een paar keer gezien hebt hoe XSD.exe je classes genereerd, kun je daarna zelf gemakkelijk je objecten maken & taggen.

Suc6!

NKCSS - Projects - YouTube


Acties:
  • 0 Henk 'm!

  • Korben
  • Registratie: Januari 2001
  • Laatst online: 13-07 01:53

Korben

() => {};

C#:
1
2
3
4
XDocument document = XDocument.Load(stream);
XNamespace ns = "http://www.mediawiki.org/xml/export-0.3/";

var titles = document.Descendants(ns + "title");


Zo 'hard-coded' hoeft het dus niet.
Kijk eens naar de XSD.exe tool.

Daarmee kan je van je XML file een .XSD genereren en vanuit de .XSD een .cs of .vb class. Deze is dan zo ingericht dat je met XMLSerializer je XML file naar een managed object kunt deserializen, je kan je doc dan gewoon refereren aan mediawiki.Page.Title om je title op te halen bijvoorbeeld.
Mijn ervaring met XSD is dat het extreem traag is als je veel data wil inlezen.

.oisyn: Échte programmeurs haten PHP met een passie. Ben jij soms geen echte programmeur?


Acties:
  • 0 Henk 'm!

  • _Thanatos_
  • Registratie: Januari 2001
  • Laatst online: 05-09 14:39

_Thanatos_

Ja, en kaal

Topicstarter
pedorus schreef op donderdag 05 augustus 2010 @ 23:57:
Als je XPath wil gebruiken, waarom kijk je dan naar linq? :p
Omdat ik hier nogal jeukerig van werd:
http://social.msdn.micros...bc-4026-96f6-a5c53f60b53f
Wat een bloated baggercode omdat het framework besluit dat je ALTIJD een namespace moet definieren, omdat een xml-bestand er toevallig een specificeert. Terwijl je zou zeggen dat als je een node "title" selecteert zonder namespace, dat ie alle "title" nodes selecteert ongeacht de namespace waar ze in zitten. Bij een selectie-techniek als CSS is een prefix verzinnen voor een default namespace ook niet verplicht.
Iets als eerste google-hit op c# xpath zal vast werken, en er hoeft helemaal niks met XML-namespaces te gebeuren.
Zo had ik het nog niet geprobeerd. Maar werkt dat dan ook voor xml-documenten waarin een default namespace gespecificeerd is?
Als dit overigens om een complete wiki in een bestand gaat, kun je trouwens beter kijken naar XmlReader (direct) ivm geheugengebruik.
Dat is niet het geval. Het gaat om losse artikelen. De wiki die ik wil uitlezen, biedt geen complete xml-dumps.
Kijk eens naar de XSD.exe tool.
Mijn ervaring met XSD is dat het extreem traag is als je veel data wil inlezen.
Meh. Teveel overhead en met weer een extra bestandje om te parsen weer een extra ding dat mis kan gaan. Daarnaast hoef ik maar een paar nodes te selecteren, waardoor zo'n xsd eromheen nogal... overheaderig aandoet. En is xsd.exe dan traag bij veel kleine xml-documenten, of juist bij grote xml-documenten?
Zo 'hard-coded' hoeft het dus niet.
Ja nou, ik zie de namespace hardcoded in je code staan. Daarnaast lijkt het dat je em bij iedere node-selectie (attributes ook?) moet string-concatten. Dat is voor 1 node niet zo erg, maar als er een paar achter elkaar staan, wordt het wel erg redundant en dus onoverzichtelijk. Het zou al wel helpen als je de default namepace uit het xml-document op binnenhengelen... Maar zelfs dat is slechts een workaround voor een probleem dat niet eens in mijn code bestaat :?

日本!🎌


Acties:
  • 0 Henk 'm!

  • pedorus
  • Registratie: Januari 2008
  • Niet online
_Thanatos_ schreef op vrijdag 06 augustus 2010 @ 13:17:
Zo had ik het nog niet geprobeerd. Maar werkt dat dan ook voor xml-documenten waarin een default namespace gespecificeerd is?
Ah, ik had helemaal over het probleem heengekeken. Tsja, als je echt niks met namespaces wil, dan kun je het volgende doen:
C#:
1
2
3
4
5
6
7
8
9
10
            var xml=@"<mediawiki xmlns=""http://www.mediawiki.org/xml/export-0.3/"">
                        <page>
                          <title>Main Page</title>
                          <id>6</id>
                        </page>
                      </mediawiki>";
            var xpd = new XPathDocument(new StringReader(xml));
            var nav = xpd.CreateNavigator();
            foreach (var title in nav.Select("//*[name()='title']"))
                Console.WriteLine(title);

Maar eigenlijk boeien die 2 regels voor die NameSpaceManager toch ook niet zoveel lijkt me..

Vitamine D tekorten in Nederland | Dodelijk coronaforum gesloten


Acties:
  • 0 Henk 'm!

  • Korben
  • Registratie: Januari 2001
  • Laatst online: 13-07 01:53

Korben

() => {};

_Thanatos_ schreef op vrijdag 06 augustus 2010 @ 13:17:
[...]Ja nou, ik zie de namespace hardcoded in je code staan.
Ik zie de namen van al de elementen die jij wil hebben ook hard in je code staan. Wat is je punt? Dit is de meest elegante optie met XDocument in ieder geval.
[...]Maar zelfs dat is slechts een workaround voor een probleem dat niet eens in mijn code bestaat [...]
Heb je het al geprobeerd? Dit werkt gewoon. Je mag anders ook gewoon zelf een XML-parser schrijven in plaats van te mekkeren over de geleverde oplossingen.

.oisyn: Échte programmeurs haten PHP met een passie. Ben jij soms geen echte programmeur?


Acties:
  • 0 Henk 'm!

  • _Thanatos_
  • Registratie: Januari 2001
  • Laatst online: 05-09 14:39

_Thanatos_

Ja, en kaal

Topicstarter
Ik zie de namen van al de elementen die jij wil hebben ook hard in je code staan.
Als hij in dat geval een node niet kan vinden, dan is dat omdat hij er niet meer is. Nu kan hij een node niet vinden ondanks dat hij er is. Dat is een wezenlijk verschil.
Dit werkt gewoon. Je mag anders ook gewoon zelf een XML-parser schrijven in plaats van te mekkeren over de geleverde oplossingen.
Nouja, mekkeren is wel een groot woord. Ik vind het gewoon een serieus probleem in de parser/framework. Er zal vast een hele goeie reden zijn dat je je hele code moet omschrijven doordat ergens in de xml opeens xmlns=".." staat, maar ik zie daar het voordeel niet van.

Ik probeer gewoon tot een elegante workaround te komen en jij noemt het mekkeren. Ik noem het perfectionisme. Ik ga niet m'n selectors aanpassen, want straks is de namespace er een keer niet en dan kan ik weer alles gaan omschrijven. Het is ook niet logisch om een xml-document en de code zo onlosmakelijk te verbinden, want daarmee spel je m.i. de hele X van XML door de plee.

@pedorus
Jouw workaround om de selectors aan te passen blijft maar raar en maakt het moeilijk leesbaar, maarja. Het lijkt wel de enige oplossing waarin voorkomen wordt dat de code kan breken doordat het xml-document verandert. De oplossing die ik gaf werkt uiteraard ook wel, maar die moet elke keer door het hele document wandelen, en XPath kan gecompileerd worden. Ik ben benieuwd wat sneller zou zijn...

日本!🎌


Acties:
  • 0 Henk 'm!

  • Snake
  • Registratie: Juli 2005
  • Laatst online: 07-03-2024

Snake

Los Angeles, CA, USA

Heel simpel: Neem 2 keer een attribuut op in een element, met beiden dezelfde naam, maar uit een andere namespace. Daarom is dat.

Going for adventure, lots of sun and a convertible! | GMT-8


Acties:
  • 0 Henk 'm!

  • pedorus
  • Registratie: Januari 2008
  • Niet online
_Thanatos_ schreef op vrijdag 06 augustus 2010 @ 15:07:
Ik ben benieuwd wat sneller zou zijn...
C#:
7
8
9
            using (var reader = XmlReader.Create(new StringReader(xml)))
                while (reader.ReadToFollowing("title"))
                    Console.WriteLine(reader.ReadElementContentAsString());

Vitamine D tekorten in Nederland | Dodelijk coronaforum gesloten


Acties:
  • 0 Henk 'm!

  • _Thanatos_
  • Registratie: Januari 2001
  • Laatst online: 05-09 14:39

_Thanatos_

Ja, en kaal

Topicstarter
Snake schreef op vrijdag 06 augustus 2010 @ 15:12:
Heel simpel: Neem 2 keer een attribuut op in een element, met beiden dezelfde naam, maar uit een andere namespace. Daarom is dat.
Waarom selecteert ie ze dan niet allebei?
pedorus schreef op vrijdag 06 augustus 2010 @ 16:07:
[...]

C#:
7
8
9
            using (var reader = XmlReader.Create(new StringReader(xml)))
                while (reader.ReadToFollowing("title"))
                    Console.WriteLine(reader.ReadElementContentAsString());
Oeh dat ziet er wel snel uit :P

日本!🎌

Pagina: 1