цикл foreach с SelectSingleNode всегда возвращает первый узел

Я пытаюсь получить внутренний текст каждого узла домена из ответа httpxml. Я сохраняю ответ в строку и загружаю в XmlDocument. Но, используя приведенный ниже код или его варианты, я получаю либо «CorpDomainMyDomain aaaa», либо просто «CorpDomain aaaa». Я пробовал различные итерации домена и доменов и не могу получить домены по отдельности. я бы подумал, что

XmlNodeList elemList = xmlDoc.SelectNodes("//DAV:domains", nsmgr); 

создал бы список каждого из элементов домена, но это не так.

XML:

<?xml version="1.0" encoding="UTF-8" ?>
  <multistatus xmlns="DAV:">
   <response>

    <propstat>
        <prop>
            <domains>
                <domain logindefault="1" type="internal"><![CDATA[CorpDomain]]></domain>
                <domain type="internal"><![CDATA[MyDomain]]></domain>
            </domains>
        </prop>
    <status>HTTP/1.1 200 OK</status>
    </propstat>
</response>

My code snippet

var nsmgr = new XmlNamespaceManager(xmlDoc.NameTable);
nsmgr.AddNamespace("DAV", "DAV:");
XmlNodeList elemList = xmlDoc.SelectNodes("//DAV:domains", nsmgr);

foreach (XmlNode node in elemList)
{
  strNode = node.SelectSingleNode("//DAV:domains", nsmgr).InnerText;
  responseString = strNode+" aaaa  ";
}

return responseString;

person Hammertime    schedule 19.02.2018    source источник
comment
Для ясности, какой точно ваш ожидаемый результат?   -  person Iain Galloway    schedule 19.02.2018
comment
Ожидаемый результат: CorpDomain aaaa MyDomain aaaa — в конце дня я добавлю CorpDomain и MyDomain в раскрывающийся список Combobox в моей основной форме. В настоящее время просто смотрю на получение отдельных предметов   -  person Hammertime    schedule 19.02.2018
comment
Я мог бы ослепнуть, но похоже, что вы заменяете весь responseString на каждой итерации цикла: responseString = strNode+" aaaa ";. Вы уверены, что не имели в виду, например. responseString = responseString + strNode + ...;?   -  person Iain Galloway    schedule 19.02.2018
comment
Вы правы, что я переписал строку. responseString += разрешил это. Это только для быстрого теста. Final будет выглядеть примерно так: conbobox.Add.Item(responseString)   -  person Hammertime    schedule 20.02.2018
comment
Рад, что вы решили это. Голосование закрыть как не по теме (опечатка). Вы также можете обратиться к ericlippert.com/2014/ 05.03/how-to-debug-small-programs, если вы столкнетесь с подобной проблемой в будущем.   -  person Iain Galloway    schedule 20.02.2018
comment
@IainGalloway Привет, Иэн - даже если ОП исправляет упомянутую вами опечатку, его использование XPath для анализа документа неверно и приводит к объединенному (и повторяющемуся после исправления опечатки) выводу CorpDomainMyDomain aaaa. Я пояснил это в своем ответе - т. Е. Нет, пожалуйста, не закрывайте как опечатку - неправильный путь к корневому уровню в цикле является распространенной проблемой при анализе документа Xml, и это может быть полезно для будущих читателей.   -  person StuartLC    schedule 22.02.2018


Ответы (1)


(Даже после того, как вы исправили проблему responseString += strNode, на которую обратил внимание Ян, когда вы перебираете элементы domain под родительским элементом domains, вам не следует снова использовать // — это сбросит контекст в корень документа.

например если ваш XmlDocument выглядит так:

<?xml version="1.0" encoding="UTF-8" ?>
  <multistatus xmlns="DAV:">
   <response>
    <propstat>
        <prop>
            <domains>
                <domain logindefault="1" type="internal">domains1-domain1</domain>
                <domain type="internal">domains1-domain2</domain>
            </domains>
        </prop>
        <prop>
            <domains>
                <domain logindefault="1" type="internal">domains2-domain1</domain>
                <domain type="internal">domains2-domain2</domain>
            </domains>
        </prop>
       <status>HTTP/1.1 200 OK</status>
    </propstat>
   </response>
</multistatus>

ваш код на самом деле будет очищать комбинированные текстовые узлы всех дочерних узлов только первого элемента domains, то есть что-то вроде (где aaaa — ваш разделитель):

домены1-домен1 аааа домены1-домен2 аааа

Вместо этого вы должны просто указать относительный путь от родителя к дочернему элементу, то есть просто domain в вашем случае. Предполагая, что есть N domains родительских элементов, каждый с M domain дочерними элементами, если вы остановитесь на родительских узлах, вам потребуется второй уровень итерации через дочерние узлы:

var nsmgr = new XmlNamespaceManager(xmlDoc.NameTable);
nsmgr.AddNamespace("DAV", "DAV:");
XmlNodeList elemList = xmlDoc.SelectNodes("//DAV:domains", nsmgr);
foreach (var domains in elemList)
{
    foreach (var domain in domains.SelectNodes("DAV:domain", nsmgr))
    {
       strNode = domain.InnerText;
       responseString = strNode+" aaaa  ";
    }
}
return responseString;

Но если вам не нужно сохранять ссылку на родителя для других целей, вы также можете сделать это за один шаг, напрямую сведя дочерние узлы. Для больших XML-документов со многими узлами также было бы неплохо избежать проблемы конкатенации строк, например. с StringBuilder:

var sb = new StringBuilder();
foreach (XmlNode node in xmlDoc.SelectNodes("//DAV:domains/DAV:domain", nsmgr))
{
    var strNode = node.InnerText;
    sb.Append(strNode); // do the same for delimiter 
}
// use sb.ToString() here.
person StuartLC    schedule 19.02.2018
comment
Извините, но я пробовал это, и он возвращает только первый узел домена, а не второй. результат: CorpDomain aaaa - person Hammertime; 19.02.2018
comment
Для 2-го примера без использования sb результат MyDomain aaaa, так что это записывает 2-й узел, но не первый, или он может перезаписывать первый, чтобы проверить это дальше. - person Hammertime; 19.02.2018
comment
@Hammertime извиняется - я не уделял должного внимания деталям в своем первом ответе - он постоянно выбирал первый дочерний элемент domain в каждом дереве domains. Я исправил. Второй подход должен работать независимо — просто убедитесь, что вы инициализируете StringBuilder вне циклов. - person StuartLC; 19.02.2018
comment
Во втором подходе вы можете перейти непосредственно к //DAV:domain при условии, что нет других элементов с именем domain под разными родителями, которые вам нужно исключить. - person StuartLC; 19.02.2018
comment
Спасибо, я пошел со вторым подходом на данный момент. Теперь подробнее о LING to XML для чтения битов грязного XML-файла из 5000 строк и извлечения некоторых дочерних узлов родительского узла на основе известного элемента дочернего узла. - person Hammertime; 20.02.2018