it-swarm.dev

Encontrando elementos no XDocument?

Eu tenho um XML simples

<AllBands>
  <Band>
    <Beatles ID="1234" started="1962">greatest Band<![CDATA[lalala]]></Beatles>
    <Last>1</Last>
    <Salary>2</Salary>
  </Band>
  <Band>
    <Doors ID="222" started="1968">regular Band<![CDATA[lalala]]></Doors>
    <Last>1</Last>
    <Salary>2</Salary>
  </Band>
</AllBands>

Contudo , 

quando eu quero alcançar a "banda de portas" e mudar sua ID: 

  using (var stream = new StringReader(result))
            {
                XDocument xmlFile = XDocument.Load(stream);

                var query = from c in xmlFile.Elements("Band")

                            select c;
                             ...

query não tem resultados

Mas

Se eu escrevo xmlFile.Elements().Elements("Band") então ele o encontra.

Qual é o problema ?

O caminho completo da raiz é necessário?

E se sim, Por que funcionou sem especificar AllBands?

O XDocument Navigation requer que eu conheça a estrutura de nível completo até o elemento necessário?

49
Royi Namir

Elements() somente verificará os filhos diretos - que no primeiro caso é o elemento raiz, no segundo caso os filhos do elemento raiz, portanto, você obtém uma correspondência no segundo caso. Se você quiser apenas um descendente correspondente, use Descendants():

var query = from c in xmlFile.Descendants("Band") select c;

Também sugiro que você re-estruture seu Xml: O nome da banda deve ser um atributo ou valor de elemento, não o próprio nome do elemento - isso faz com que a consulta (e validação de esquema) seja muito mais difícil, por exemplo:

<Band>
  <BandProperties Name ="Doors" ID="222" started="1968" />
  <Description>regular Band<![CDATA[lalala]]></Description>
  <Last>1</Last>
  <Salary>2</Salary>
</Band>
72
BrokenGlass

Você pode fazer assim:

xml.Descendants().SingleOrDefault(p => p.Name.LocalName == "Name of the node to find")

onde xml é um XDocument.

Esteja ciente de que a propriedade Name retorna um objeto que possui um LocalName e um Namespace. É por isso que você precisa usar Name.LocalName se quiser comparar por nome.

27
Francisco Goldenstein

Você deve usar Root para se referir ao elemento raiz:

xmlFile.Root.Elements("Band")

Se você quiser encontrar elementos em qualquer parte do documento, use Descendants em vez disso:

xmlFile.Descendants("Band")
23
Mark Byers

O problema é que Elements só recebe os elementos direct do que quer que você chame. Se você quiser todos os descendentes, use o método Descendants :

var query = from c in xmlFile.Descendants("Band")
6
Jon Skeet

Minha experiência ao trabalhar com arquivos XML grandes e complicados é que às vezes nem Elementos nem Descendentes parecem funcionar na recuperação de um Elemento específico (e eu ainda não sei por quê). 

Nesses casos, descobri que uma opção muito mais segura é pesquisar manualmente o Elemento, conforme descrito pela postagem do MSDN a seguir:

https://social.msdn.Microsoft.com/Forums/vstudio/pt-BR/3d457c3b-292c-49e1-9fd4-9b6a950f9010/how-to-get-tag-name-of-xml-by-using- xdocument? forum = csharpgeneral

Em suma, você pode criar uma função GetElement:

private XElement GetElement(XDocument doc,string elementName)
{
    foreach (XNode node in doc.DescendantNodes())
    {
        if (node is XElement)
        {
            XElement element = (XElement)node;
            if (element.Name.LocalName.Equals(elementName))
                return element;
        }
    }
    return null;
}

Qual você pode então chamar assim:

XElement element = GetElement(doc,"Band");

Observe que isso retornará null se nenhum elemento correspondente for encontrado.

4
Human Wannabe

O método Elements() retorna um IEnumerable<XElement> contendo todos os elementos filho do nó atual. Para um XDocument, essa coleção contém apenas o elemento Root. Portanto, o seguinte é necessário:

var query = from c in xmlFile.Root.Elements("Band")
            select c;
2
Phil Klein

A resposta de Sebastian foi a única resposta que funcionou para mim enquanto examinava um documento xaml. Se, como eu, você quiser uma lista de todos os elementos, então o método ficaria muito parecido com a resposta de Sebastian acima, mas apenas retornando uma lista ...

    private static List<XElement> GetElements(XDocument doc, string elementName)
    {
        List<XElement> elements = new List<XElement>();

        foreach (XNode node in doc.DescendantNodes())
        {
            if (node is XElement)
            {
                XElement element = (XElement)node;
                if (element.Name.LocalName.Equals(elementName))
                    elements.Add(element);
            }
        }
        return elements;
    }

Chame assim:

var elements = GetElements(xamlFile, "Band");

ou no caso do meu xaml doc onde eu queria todos os TextBlocks, chame assim:

var elements = GetElements(xamlFile, "TextBlock");
0
Ewan