Проверьте, существует ли узел в XmlNode [дубликат]

Примером этого исключаемого исключения является: Когда вы пытаетесь проверить что-то, это null.

Например:

string testString = null; //Because it doesn't have a value (i.e. it's null; "Length" cannot do what it needs to do)

if (testString.Length == 0) // Throws a nullreferenceexception
{
    //Do something
} 

Время выполнения .NET исключение NullReferenceException при попытке выполнить действие над чем-то, что не было инстанцировано, т.е. код выше.

По сравнению с ArgumentNullException, которое обычно выбрано как защитная мера, если метод ожидает, что то, что происходит

Дополнительная информация находится в C # NullReferenceException и Null Parameter .

54
задан macleojw 25 February 2009 в 13:34
поделиться

12 ответов

Сначала вам не нужен навигатор; SelectNodes / SelectSingleNode должен быть достаточным.

Однако вам может понадобиться менеджер пространства имен - например:

XmlElement el = ...; //TODO
XmlNamespaceManager nsmgr = new XmlNamespaceManager(
    el.OwnerDocument.NameTable);
nsmgr.AddNamespace("x", el.OwnerDocument.DocumentElement.NamespaceURI);
var nodes = el.SelectNodes(@"/x:outerelement/x:innerelement", nsmgr);
74
ответ дан Marc Gravell 24 August 2018 в 07:15
поделиться
  • 1
    NB: Установка псевдонима на пустую строку (nsmgr.AddNamespace("", el.OwnerDocument.DocumentElement.NamespaceURI);) делает это пространство имен по умолчанию. Однако, к сожалению, это не означает, что вы можете использовать XPath без использования префикса (например, var nodes = el.SelectNodes(@"/outerelement/innerelement", nsmgr);). Только это вы можете увидеть с помощью nsmgr.DefaultNamespace. Подробнее здесь: stackoverflow.com/a/4271875/361842 . Комментарий добавлен, чтобы сэкономить время, если вы хотите избежать использования префиксов; т. е. вы не можете. – JohnLBevan 6 April 2017 в 17:54
  • 2
    еще один взлом, если вы просто замените xmlns= на xmlns:p, где p может быть любым допустимым префиксом, ваш код должен работать как есть. – Ravi M Patel 13 December 2017 в 14:04
  • 3
    Я пробовал ваш код, но Visual Studio говорит, что XElement does not contain a definition for OwnerDocument..., не могли бы вы посмотреть на него? imgur.com/a/TPHVeoM – Luke 28 June 2018 в 17:39

Возможно, вы захотите попробовать инструмент XPath Visualizer, чтобы помочь вам.

XPathVisualizer является бесплатным, простым в использовании.

ВАЖНО: Если вы используете Windows 7/8 и не видите пункты меню «Файл», «Редактировать» и «Справка», нажмите клавишу ALT.

48
ответ дан adrianbanks 24 August 2018 в 07:15
поделиться

Вы можете использовать инструкцию XPath без использования XmlNamespaceManager следующим образом:

...
navigator.Select("//*[ local-name() = 'innerelement' and namespace-uri() = '' ]")
...

Это простой способ выбора элемента в XML с определением пространства имен по умолчанию. Дело в том, чтобы использовать:

namespace-uri() = ''

, который найдет элемент с пространством имен по умолчанию без использования префиксов.

6
ответ дан Bartosz Węgielewski 24 August 2018 в 07:15
поделиться
  • 1
    namespace-uri = '' не работает для меня, но это дало мне идею динамически создать выражение xpath следующим образом: doc.SelectNodes (String.Format (& quot; // * [local-name () = 'innerelement 'и namespace-uri () =' {0} '] ", doc.DocumentElement.NamespaceURI)); и что работает – stefann 6 September 2013 в 03:30

Я столкнулся с аналогичной проблемой с пустым пространством имен по умолчанию. В этом примере XML у меня есть сочетание элементов с префиксами пространства имен и один элемент (DataBlock) без:

<src:SRCExample xmlns="urn:some:stuff:here" xmlns:src="www.test.com/src" xmlns:a="www.test.com/a" xmlns:b="www.test.com/b">
 <DataBlock>
  <a:DocID>
   <a:IdID>7</a:IdID>
  </a:DocID>
  <b:Supplimental>
   <b:Data1>Value</b:Data1>
   <b:Data2/>
   <b:Extra1>
    <b:More1>Value</b:More1>
   </b:Extra1>
  </b:Supplimental>
 </DataBlock>
</src:SRCExample>

Я попытался использовать XPath, который работал в XPath Visualizer, но не работал в моем коде:

  XmlDocument doc = new XmlDocument();
  doc.Load( textBox1.Text );
  XPathNavigator nav = doc.DocumentElement.CreateNavigator();
  XmlNamespaceManager nsman = new XmlNamespaceManager( nav.NameTable );
  foreach ( KeyValuePair<string, string> nskvp in nav.GetNamespacesInScope( XmlNamespaceScope.All ) ) {
    nsman.AddNamespace( nskvp.Key, nskvp.Value );
  }

  XPathNodeIterator nodes;

  XPathExpression failingexpr = XPathExpression.Compile( "/src:SRCExample/DataBlock/a:DocID/a:IdID" );
  failingexpr.SetContext( nsman );
  nodes = nav.Select( failingexpr );
  while ( nodes.MoveNext() ) {
    string testvalue = nodes.Current.Value;
  }

Я сузил его до элемента «DataBlock» XPath, но не смог заставить его работать, за исключением простого подстановки элемента DataBlock:

  XPathExpression workingexpr = XPathExpression.Compile( "/src:SRCExample/*/a:DocID/a:IdID" );
  failingexpr.SetContext( nsman );
  nodes = nav.Select( failingexpr );
  while ( nodes.MoveNext() ) {
    string testvalue = nodes.Current.Value;
  }

После того, как я много раз набрал чек и Google (я приземлился здесь), я решил заняться пространством имен по умолчанию непосредственно в моем загрузчике XmlNamespaceManager, изменив его на:

  foreach ( KeyValuePair<string, string> nskvp in nav.GetNamespacesInScope( XmlNamespaceScope.All ) ) {
    nsman.AddNamespace( nskvp.Key, nskvp.Value );
    if ( nskvp.Key == "" ) {
      nsman.AddNamespace( "default", nskvp.Value );
    }
  }

Итак, теперь точка «по умолчанию» и «» к тому же пространству имен. Как только я это сделал, XPath «/ src: SRCExample / default: DataBlock / a: DocID / a: IDID» вернул мои результаты так, как я хотел. Надеюсь, это поможет прояснить проблему для других.

5
ответ дан Brandon 24 August 2018 в 07:15
поделиться

В этом случае это, вероятно, разрешение пространства имен, которое является причиной проблемы, но также возможно, что ваше выражение XPath само по себе не является правильным. Вы можете сначала оценить его.

Вот код с помощью XPathNavigator.

//xNav is the created XPathNavigator.
XmlNamespaceManager mgr = New XmlNamespaceManager(xNav.NameTable);
mgr.AddNamespace("prefix", "http://tempuri.org/");

XPathNodeIterator result = xNav.Select("/prefix:outerelement/prefix:innerelement", mgr);
0
ответ дан Cerebrus 24 August 2018 в 07:15
поделиться

Я использовал взломанный, но полезный подход, описанный SpikeDog выше. Он работал очень хорошо, пока я не бросил в него выражение xpath, которое использовало каналы для объединения нескольких путей.

Итак, я переписал его с использованием регулярных выражений и думал, что делюсь:

public string HackXPath(string xpath_, string prefix_)
{
    return System.Text.RegularExpressions.Regex.Replace(xpath_, @"(^(?![A-Za-z0-9\-\.]+::)|[A-Za-z0-9\-\.]+::|[@|/|\[])(?'Expression'[A-Za-z][A-Za-z0-9\-\.]*)", x =>
                {
                    int expressionIndex = x.Groups["Expression"].Index - x.Index;
                    string before = x.Value.Substring(0, expressionIndex);
                    string after = x.Value.Substring(expressionIndex, x.Value.Length - expressionIndex);
                    return String.Format("{0}{1}:{2}", before, prefix_, after);
                });
}
1
ответ дан Dan 24 August 2018 в 07:15
поделиться
  • 1
    Эта версия имеет проблемы, когда выражения пути имеют атрибуты. Например, элемент / @ id & quot; преобразуется в "p: element / p: @ id" когда это должно быть «p: element / @ id». – DaniCE 21 December 2011 в 19:30

Мой ответ расширяет предыдущий ответ Брэндона. Я использовал свой пример для создания метода расширения следующим образом:

static public class XmlDocumentExt
{
    static public XmlNamespaceManager GetPopulatedNamespaceMgr(this System.Xml.XmlDocument xd)
    {
        XmlNamespaceManager nmsp = new XmlNamespaceManager(xd.NameTable);
        XPathNavigator nav = xd.DocumentElement.CreateNavigator();
        foreach (KeyValuePair<string,string> kvp in nav.GetNamespacesInScope(XmlNamespaceScope.All))
        {
            string sKey = kvp.Key;
            if (sKey == "")
            {
                sKey = "default";
            }
            nmsp.AddNamespace(sKey, kvp.Value);
        }

        return nmsp;
    }
}

Затем в моем XML-синтаксическом коде я просто добавляю одну строку:

XmlDocument xdCandidate = new XmlDocument();
xdCandidate.Load(sCandidateFile);
XmlNamespaceManager nmsp = xdCandidate.GetPopulatedNamespaceMgr();  // 1-line addition
XmlElement xeScoreData = (XmlElement)xdCandidate.SelectSingleNode("default:ScoreData", nmsp);

Мне очень нравится это потому что он полностью динамичен с точки зрения загрузки пространств имен из исходного XML-файла и не полностью игнорирует концепцию пространств имен XML, поэтому это можно использовать с XML, для которого требуется несколько пространств имен для deconfliction.

4
ответ дан Kent 24 August 2018 в 07:15
поделиться

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

XmlDocument doc = new XmlDocument();
string fileData = File.ReadAllText(fileName);
fileData = fileData.Replace(" xmlns=\"", " whocares=\"");
using (StringReader sr = new StringReader(fileData))
{
   doc.Load(sr);
}

XmlNodeList nodeList = doc.SelectNodes("project/property");

Я нахожу это проще, чем все другие не-чувства, требующие префикса для пространства имен по умолчанию, когда я имею дело с одним файлом. Надеюсь, это поможет.

25
ответ дан Mitselplik 24 August 2018 в 07:15
поделиться
  • 1
    Это блестяще. Все остальные BS, связанные с XmlNamespaceManager, бесполезны. 9999 раз из 10 000 вы знаете XML. – Scott Shaw-Smith 17 September 2015 в 22:40
  • 2
    Хотелось бы, чтобы я мог продвигать это 10 раз, очень простое решение, это отлично работает !!! – Nabeel Bape 3 February 2016 в 08:09
  • 3
    Единственным недостатком является то, что, как и ожидалось, выбранные элементы XML находятся в нулевом пространстве имен. Хотя мне действительно нравится этот хак, если пространство имен является требованием вашей работы, это не будет соответствовать счету. – Timothy 28 June 2016 в 17:19
  • 4
    «Глупость» не имеет ничего общего с одним файлом - это связано с тегами namespacing. Если вы контролируете XML, тогда вам не нужно использовать пространства имен (теги будут существовать в пустом пространстве имен). Если вы не контролируете ситуацию, вы создаете взлом для решения, требующего 1/2 кода. И отметил Тимоти, теперь у вас будет два разных решения, основанных на том, можете ли вы воспользоваться возможностью не повторять тег. Потому что вы хотели сохранить две строки и использовали 4 строки для этого. – Gerard ONeill 31 August 2016 в 18:08
  • 5
    @Gerard - Я не пытался попасть под чью-то кожу. Мой пост больше связан с KISS, а не насмешкой. В любом случае: (1) я назвал свое решение взломом, подразумевая, что это не «правильный» подход; (2) Независимо от того, контролирует ли моя аудитория XML, я явно указал, что это только хорошее решение, если вы знаете XML и не должны беспокоиться о пространствах имен. (3) Хотя может быть правдой, что для включения в диспетчер и только пространств имен требуется только несколько дополнительных строк, сами строки XPath выглядят действительно беспорядочно со всем лишним шумом пространства имен, загромождающим их. – Mitselplik 16 January 2017 в 15:35

В случае, если пространства имен отличаются для outerelement и innerelement

XmlNamespaceManager manager = new XmlNamespaceManager(myXmlDocument.NameTable);
                            manager.AddNamespace("o", "namespaceforOuterElement");
                            manager.AddNamespace("i", "namespaceforInnerElement");
string xpath = @"/o:outerelement/i:innerelement"
// For single node value selection
XPathExpression xPathExpression = navigator.Compile(xpath );
string reportID = myXmlDocument.SelectSingleNode(xPathExpression.Expression, manager).InnerText;

// For multiple node selection
XmlNodeList myNodeList= myXmlDocument.SelectNodes(xpath, manager);
5
ответ дан Rashmi Pandit 24 August 2018 в 07:15
поделиться

В моем случае добавление префикса было непрактичным. Слишком большая часть xml или xpath была определена во время выполнения. В конце концов я расширил методы на XmlNode. Это не было оптимизировано для производительности, и, вероятно, оно не обрабатывает все случаи, но оно работает для меня до сих пор.

    public static class XmlExtenders
{

    public static XmlNode SelectFirstNode(this XmlNode node, string xPath)
    {
        const string prefix = "pfx";
        XmlNamespaceManager nsmgr = GetNsmgr(node, prefix);
        string prefixedPath = GetPrefixedPath(xPath, prefix);
        return node.SelectSingleNode(prefixedPath, nsmgr);
    }

    public static XmlNodeList SelectAllNodes(this XmlNode node, string xPath)
    {
        const string prefix = "pfx";
        XmlNamespaceManager nsmgr = GetNsmgr(node, prefix);
        string prefixedPath = GetPrefixedPath(xPath, prefix);
        return node.SelectNodes(prefixedPath, nsmgr);
    }

    public static XmlNamespaceManager GetNsmgr(XmlNode node, string prefix)
    {
        string namespaceUri;
        XmlNameTable nameTable;
        if (node is XmlDocument)
        {
            nameTable = ((XmlDocument) node).NameTable;
            namespaceUri = ((XmlDocument) node).DocumentElement.NamespaceURI;
        }
        else
        {
            nameTable = node.OwnerDocument.NameTable;
            namespaceUri = node.NamespaceURI;
        }
        XmlNamespaceManager nsmgr = new XmlNamespaceManager(nameTable);
        nsmgr.AddNamespace(prefix, namespaceUri);
        return nsmgr;
    }

    public static string GetPrefixedPath(string xPath, string prefix)
    {
        char[] validLeadCharacters = "@/".ToCharArray();
        char[] quoteChars = "\'\"".ToCharArray();

        List<string> pathParts = xPath.Split("/".ToCharArray()).ToList();
        string result = string.Join("/",
                                    pathParts.Select(
                                        x =>
                                        (string.IsNullOrEmpty(x) ||
                                         x.IndexOfAny(validLeadCharacters) == 0 ||
                                         (x.IndexOf(':') > 0 &&
                                          (x.IndexOfAny(quoteChars) < 0 || x.IndexOfAny(quoteChars) > x.IndexOf(':'))))
                                            ? x
                                            : prefix + ":" + x).ToArray());
        return result;
    }
}

Затем в вашем коде просто используйте что-то вроде

        XmlDocument document = new XmlDocument();
        document.Load(pathToFile);
        XmlNode node = document.SelectFirstNode("/rootTag/subTag");

Надеюсь, это поможет

3
ответ дан SpikeDog 24 August 2018 в 07:15
поделиться
  • 1
    Я использовал этот код, и он работал как шарм, пока я не столкнулся с проблемой сегодня. Он не обрабатывает выражения xpath, которые используют этот канал. Поскольку я обнаружил, что исходный код трудно читать, я переписал его с помощью регулярных выражений, которые мне легче найти (см. Мой ответ ниже) – Dan 20 October 2010 в 17:06

При использовании XPath в .NET (через навигатор или SelectNodes / SelectSingleNode) в XML с пространствами имен вам необходимо:

  • предоставить собственный XmlNamespaceManager
  • и явно префикс все элементы в выражении XPath, которые находятся в пространстве имен.

Последнее (перефразировано из источника MS, связанного ниже): поскольку XPath 1.0 игнорирует спецификации пространства имен по умолчанию (xmlns = "some_namespace"). Поэтому, когда вы используете имя элемента без префикса, он принимает пустое пространство имен.

Вот почему реализация .NET XPath игнорирует пространство имен с префиксом String.Empty в XmlNamespaceManager и всегда использует пустое пространство имен.

См. XmlNamespaceManager и UndefinedXsltContext не обрабатывают пространство имен по умолчанию для получения дополнительной информации.

Я нахожу эту «функцию» очень неудобной, потому что вы не можете создавать старые пространства имен XPath, просто добавляя декларацию пространства имен по умолчанию , но вот как это работает.

19
ответ дан Tomek Szpakowicz 24 August 2018 в 07:15
поделиться
  • 1
    Вы пишете XPath 1.0 игнорирует пространство имен по умолчанию . Это неверно. Вы игнорируете его, если вы используете /root/child, потому что неперечисленный тест QName выбирает элементы под пустым или пустым пространством имен по определению . – user 30 December 2010 в 21:17
  • 2
    Правильно говоря, QName является кортежем (URI пространства имен, локальное имя, префикс). Таким образом, этот элемент <el xmlns="URI"/> имеет QName ('URI', 'el', ''), эквивалентный этому другому элементу <pre:el xmlns:pre="URI"/> ('URI', 'el', 'pre'), но отличается от этого последнего элемента <el xmlns:pre="URI"/> ('', 'el', '') – user 3 January 2011 в 17:13
  • 3
    @Alejandro: После рассмотрения я решил удалить мои комментарии, потому что считаю это обсуждение бессмысленным. Если мой ответ недостаточно точен, напишите лучше. Если мой ответ неверен, предоставьте рабочий пример, который показывает его. – Tomek Szpakowicz 5 January 2011 в 16:16
  • 4
    Он не игнорирует пространства имен по умолчанию. Вы просто не можете указать пространство имен по умолчанию. Огромная разница. И разница имеет смысл - пространство имен по умолчанию для любого заданного тега может быть другим; xpath должен перейти к явному тегу. Если вы не используете имя тега, которое вы можете сделать. Но он найдет все теги, которые определены с пространством имен по умолчанию; вам просто нужно указать это пространство имен с тегом в выражении xpath. – Gerard ONeill 31 August 2016 в 17:50
  • 5
    @GerardONeill Мой предыдущий комментарий, адресованный user357812 (aka Alejandro), по-прежнему применяется. Кроме того, я думаю, вы смешиваете спецификации пространства имен, включенные в документ XML, с спецификациями пространства имен, которые применяются к самому выражению XPath. Мой ответ о последнем. – Tomek Szpakowicz 6 September 2016 в 11:26

Или, если кто-то должен использовать XPathDocument, например me:

XPathDocument xdoc = new XPathDocument(file);
XPathNavigator nav = xdoc.CreateNavigator();
XmlNamespaceManager nsmgr = new XmlNamespaceManager(nav.NameTable);
nsmgr.AddNamespace("y", "http://schemas.microsoft.com/developer/msbuild/2003");
XPathNodeIterator nodeIter = nav.Select("//y:PropertyGroup", nsmgr);
1
ответ дан Zak 24 August 2018 в 07:15
поделиться
Другие вопросы по тегам:

Похожие вопросы: