¿Cómo se manejan espacios de nombres arbitrarios al realizar consultas de Linq a XML?
Tengo un proyecto en el que estoy tomando un HTML "en vivo" particularmente feo y forzándolo a convertirlo en un DOM XML formal con HTML Agility Pack. Lo que me gustaría poder hacer es consultar esto con Linq to XML para poder extraer los bits que necesito. Estoy usando el método descrito aquí para analizar el HtmlDocument en un XDocument, pero cuando intento consultar esto, no estoy seguro de cómo manejar los espacios de nombres. En un documento en particular, el HTML original en realidad tenía un formato XHTML deficiente con la siguiente etiqueta:
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en">
Al intentar realizar una consulta desde este documento, parece que el atributo de espacio de nombres me impide hacer algo como:
var x = xDoc.Descendants("div");
// returns null
Aparentemente, para esas etiquetas "div" solo el nombre local es "div", pero el nombre de etiqueta adecuado es el espacio de nombres más "div". Intenté investigar un poco sobre el tema de los espacios de nombres XML y parece que puedo omitir el espacio de nombres consultando de esta manera:
var x =
(from x in xDoc.Descendants()
where x.Name.LocalName == "div"
select x);
// works
Sin embargo, esto parece una solución bastante complicada y no aborda adecuadamente el problema del espacio de nombres. Según tengo entendido, un documento XML adecuado puede contener múltiples espacios de nombres y, por lo tanto, la forma correcta de manejarlo debería ser analizar los espacios de nombres que estoy consultando. ¿Alguien más ha tenido que hacer esto alguna vez? ¿Lo estoy haciendo demasiado complicado? Sé que podría evitar todo esto si me quedo con HtmlDocument y consulta con XPath, pero prefiero ceñirme a lo que sé (Linq) si es posible y también preferiría saber que no me estoy preparando para más espacios de nombres. cuestiones relacionadas en el futuro.
¿Cuál es la forma correcta de lidiar con los espacios de nombres en esta situación?
Usarlo LocalName
debería estar bien. No lo consideraría un truco en absoluto si no te importa en qué espacio de nombres se encuentra.
Si conoce el espacio de nombres que desea y desea especificarlo, puede:
var ns = "{http://www.w3.org/1999/xhtml}";
var x = xDoc.Root.Descendants(ns + "div");
( referencia de MSDN )
También puede obtener una lista de todos los espacios de nombres utilizados en el documento:
var namespaces = (from x in xDoc.Root.DescendantsAndSelf()
select x.Name.Namespace).Distinct();
Supongo que podrías usar eso para hacer esto, pero en realidad no es menos truco:
var x = namespaces.SelectMany(ns=>xDoc.Root.Descendants(ns+"div"));
Si sabe que el espacio de nombres será declarado por el elemento raíz del XML, como suele ser el caso, puede hacer esto:
var ns = xDoc.Root.Name.Namespace;
var x = xDoc.Descendants(ns + "div");