Ile razy spotkaliście się, że wam to po prostu nie działa? Wykonujecie prosty kod:

var list = root.SelectNodes("/blog//posts//post/comments/comment[@user-name='Gutek']");

a lista zawiera zero elementów? Patrzycie w XML i mówicie, nie możliwe.

A jednak. Po 12h pracy wróciłem do domu i stwierdziłem, że pora zabrać się za przenoszenie postów – niby wszystko miało być pięknie. Posty wyeksportowałem do standardu BlogML za pomocą małego rozszerzenia do Community Server. Dane wyeksportowane jednakże zawierały pewne moim zdaniem bugi – na przykład, zamiast nazwy autora komentarza było podane „http://”, atrybut e-mail autora komentarza był pominięty, zaś wszystkie znaki specjalne takie jak <> czy & mimo CDATA były konwertowane do postaci & amp; itp. Podliczyłem liczbę zmian jakie muszę wykonać w tym stworzenie atrybutów zmiana treści i stwierdziłem iż najprostszym i najszybszym sposobem będzie wykorzystanie do tego wszystkiego C# – w końcu jesteśmy programistami? :)

Właśnie mijała 15h mojej egzystencji i wiedziałem, że następnego dnia nie będzie lepiej, więc nie myśląc zbytnio o jakiś super ekstra rozwiązaniu sięgnąłem po klasę XmlDocument, w której myślałem, że wszystko szybko załatwię. Nic bardziej błędnego.

Po 30 minutach zacząłem sobie wyrwać włosy z głowy, albo ja byłem kompletnie głupi i napisałem błędny kod, albo po prostu ktoś popełnił wielki błąd w BCL – co po prawie 16h uważałem za coraz bardziej prawdopodobne :)

Zanim jednak się poddałem, zacząłem przeglądać Google w poszukiwaniu rozwiązania – znalazłem wiele opisów tego samego problemu ale żaden nie dawał rozwiązania. Moja wiara w Bug BCL z każdą minutę rosła. Głos rozsądku jednak podpowiadał – gdyby XmlDocument i SelectNodes nie działały poprawnie to po MS nie została by przecież sucha nitka, a i Google by dał wiele setek tysięcy wyników mówiących jako to MS jest skoro nie wspiera poprawnie XmlDocument.

W końcu natrafiłem na mała zmiankę, że XmlDocument wymaga namespace by działał poprawnie. Więc przejrzałem się swojemu xml’owi i rzeczywiście namespace był zadeklarowany, ale był jako domyślny . Idąc dalej tym tropem w końcu natrafiłem na artykuł starający się wytłumaczyć dlaczego tak się dzieje. Dalsze poszukiwania dały tylko i wyłącznie jedyną poprawną odpowiedź: XPath 1.0 nie wspiera tak zwanych Default Namespace przez co za każdym razem kiedy robimy zapytanie musimy podać zarówno prefix jak i namespace.

Wykonanie więc poniższego kodu, rozwiązało cały problem jaki miałem:

var xmlns = _xmlDoc.DocumentElement.Attributes["xmlns"].Value;

var nsmgr = new XmlNamespaceManager(_xmlDoc.NameTable);

nsmgr.AddNamespace("BlogMl", xmlns);

var root = _xmlDoc.DocumentElement;

var list = root.SelectNodes("/BlogMl:blog//BlogMl:posts//BlogMl:post/BlogMl:comments/BlogMl:comment[@user-name='Gutek']", nsmgr);

Więc ogólnie pamiętajcie, jeżeli wasz XML zawiera atrybut xmlns to zastanówcie się nad wykorzystaniem innego sposobu niż XmlDocument lub dodajcie namespace :)

2 KOMENTARZE

Comments are closed.