Избегайте исключения пустой ссылки на объект при доступе к подэлементам, которые могут существовать или не существовать - PullRequest
3 голосов
/ 08 марта 2011

У меня есть: XML с некоторыми элементами.Подэлемент, который может или не может быть определен внутри этого XML.Необходимо извлечь значение подэлемента, когда он существует.

Как получить значение без выдачи ошибок ссылки на объект?

Например:

 string sampleXML = "<Root><Tag1>tag1value</Tag1></Root>"; 

//Pass in <Tag2> and the code works: 
//string sampleXML = "<Root><Tag1>tag1value</Tag1><Tag2>tag2Value</Tag2></Root>";    
 XDocument sampleDoc = XDocument.Parse(sampleXML);

//Below code is in another method, the 'sampleDoc' is passed in. I am hoping to change only this code
XElement sampleEl = sampleDoc.Root; 
string tag1 = String.IsNullOrEmpty(sampleEl.Element("Tag1").Value) ? "" : sampleEl.Element("Tag1").Value;

//NullReferenceException:
//Object reference not set to an instance of an object.
string tag2 = String.IsNullOrEmpty(sampleEl.Element("Tag2").Value) ? "" : sampleEl.Element("Tag2").Value;

Ответы [ 8 ]

10 голосов
/ 08 марта 2011

Вы можете использовать оператор null-coalescing для ярлыка:

 string tag1= (string)sampleEl.Element("Tag1") ?? string.Empty;

При этом также используется тот факт, что LINQ to XML позволяет операции приведения получить значение элемента (в данном случае приведение к строке), но возвращает null, если элемент не существует.

3 голосов
/ 08 марта 2011

Тернарный оператор C # довольно хорош для этого:

string tag2 = sampleEl.Element("Tag2") == null ? "" : sampleEl.Element("Tag2").Value;
3 голосов
/ 08 марта 2011

Вам нужно будет проверить на null, чтобы предотвратить их.Учитывая повторяющийся шаблон, который вы используете, я бы просто включил это в метод расширения.

public static string GetElementValue(this XElement parent, string elementName) {
  if (parent == null) { 
    return string.Empty;
  }
  var element = parent.Element(elementName);
  if (element == null || element.Value == null) {
    return string.Empty;
  }
  return element.Value;
}

Теперь ваш вышеприведенный код можно заменить следующим

string tag1 = sampleEl.GetElementValue("Tag1");
string tag2 = sampleEl.GetElementValue("Tag2");
2 голосов
/ 08 марта 2011

Сначала вы должны проверить, является ли документ нулевым, помните, что вы получаете доступ к .Value, и это вызовет исключение нулевой ссылки, поэтому перед применением .value выполните тест:

if (sampleEl != null)
  //now apply .value

Или троичный:

string tag2 = sampleEl.Element("Tag2") != null ? sampleEL.Element("Tag2").Value : String.Empty

Ваш код становится:

 string sampleXML = "<Root><Tag1>tag1value</Tag1></Root>"; 

    //Pass in <Tag2> and the code works: 
    //string sampleXML = "<Root><Tag1>tag1value</Tag1><Tag2>tag2Value</Tag2></Root>";    
     XDocument sampleDoc = XDocument.Parse(sampleXML);

    //Below code is in another method, the 'sampleDoc' is passed in. I am hoping to change only this code
    XElement sampleEl = sampleDoc.Root; 
    string tag1 = sampleEl.Element("Tag1") != null ? sampleEl.Element("Tag1").Value : String.Empty;

    //NullReferenceException:
    //Object reference not set to an instance of an object.
string tag2 = sampleEl.Element("Tag2") != null ? sampleEL.Element("Tag2").Value : String.Empty
1 голос
/ 06 февраля 2018

C # 6.0 позволяет нам сделать выражение короче и проще:

 string uniqueIdentifier = myNode.Document?.Element("elem1")?.Element("elem2")?.Attribute("attribute1")?.Value;
1 голос
/ 08 марта 2011

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

public static TOut ValueOrDefault<TIn, TOut>(this TIn input, Func<TIn, TOut> projection, TOut defaultValue)
        where TOut : class
    {
        try
        {
            return projection(input) ?? defaultValue;
        }
        catch (NullReferenceException)
        {
            return defaultValue;
        }
        catch (InvalidOperationException)
        {
            return defaultValue;
        }
    }

Использование:

var value = topObject.ValueOrDefault(x=>x.ChildObject.AnotherChild.ChildProperty, String.Empty);

value будет пустой строкой, если topObject, ChildObject, AnotherChild или ChildProperty равны нулю.Если все они являются допустимыми ссылками, возвращаемое значение будет таким, каким на самом деле является ChildProperty (которое может быть пустой строкой).Уловка для исключения NullReferenceException обрабатывает ссылки дочерних элементов пустой ссылки.Для обнуляемых типов InvalidOperationException выбрасывается при доступе к свойству Value пустого обнуляемого типа.

1 голос
/ 08 марта 2011
string tag1 = sampleEl.Element("Tag1") != null ? sampleEl.Element("Tag1").Value : string.Empty;
0 голосов
/ 08 марта 2011

Просто для удовольствия, вот решение, использующее LINQ to XML, которое

  • требует только одного оператора, а
  • не требует троичного оператора, поэтому выне нужно указывать имя тега дважды:

    string tag1 = sampleEl.Elements("Tag1").Select(x => x.Value).FirstOrDefault();
    string tag2 = sampleEl.Elements("Tag2").Select(x => x.Value).FirstOrDefault();
    

Добавьте ?? "", если вы хотите пустую строку вместо null, если тег не существует.

...