Как получить доступ к родительскому атрибуту XML с помощью LINQ в C#

Кажется, я не могу правильно понять синтаксис Linq - или я не знаю, как ссылаться на объекты XML.

У меня есть XML вроде

<?xml version = "1.0" encoding = "UTF-8"?>
<Targets>
    <FileType TargetId = "image">
        <FileExtension>jpg</FileExtension>
        <FileExtension>jpeg</FileExtension>
        <FileExtension>tif</FileExtension>
        <FileExtension>tiff</FileExtension>
        <FileExtension>gif</FileExtension>
        <FileExtension>png</FileExtension>
    </FileType>
</Targets>

Я хочу найти/извлечь «TargetId», где расширение файла соответствует тому, которое я ищу, например «jpeg».

Во-первых, я впервые использую структуру со списком одинаковых имен элементов FileExtension. И, кажется, никогда не находит его.

Я основываю это на предыдущем тестовом коде, где с помощью XML типа...

<body>
  <Customers>
    <Client TestAttr = ""testing-123"">
        <Firstname>someguy</Firstname>
        <LastName>some other last name again</LastName>
        <PhoneNumber>12345634543</PhoneNumber>
        <City>some other town</City>
        <State>some other state</State>
    </Client>
  </Customers>
</body>

код

 IEnumerable<XElement> someClients =
    from el in doc.Elements("Customers").Elements("Client")
    where (string)el.Element("PhoneNumber") == phoneNumberToFind
    select el;

находит того, кого ищу. тогда я могу получить другой элемент в том же «Клиенте» с помощью

Console.WriteLine("Found client named '{0}'", (string)el.Element("LastName").Value );

и печать «el» дает следующее - все находится в одном куске: -

<Client TestAttr = "testing-123">
  <Firstname>someguy</Firstname>
  <LastName>some other last name again</LastName>
  <PhoneNumber>12345634543</PhoneNumber>
  <City>some other town</City>
  <State>some other state</State>
</Client>

Во-вторых, все мои попытки получить доступ к атрибуту на родительском уровне встречают

An unhandled exception of type 'System.NullReferenceException' occurred in ......dll: 'Object reference not set to an instance of an object.'

пробовали включать в себя

string fTestAttr1=  (string)el.Element("Client").Value;
string fTestAttr2=  (string)el.Element("Client").Attribute("TestAttr");
string fTestAttr3=  (string)el.Element("Client").Attribute("TestAttr").Value; 
string fTestAttr4=  (string)el.Element("Client").FirstAttribute.Value;   
string fTestAttr4=  (string)el.Element("Client").FirstAttribute.Value)   

Так что я явно не знаю адреса родителя. Не помогает отладка VS Code, где я вижу, что «el» - это [XElement], но у него нет никакого «Element».

Спасибо за любую поддержку Джей Си

Все, что вам нужно сделать, это изменить «select el», который возвращает весь элемент в одном фрагменте, на следующее: select new { firstname = (string)el.Element("Firstname"), Lastname = (string)el.Element( "Фамилия")}; При использовании Linq Value выдаст ошибку, если у вас есть ноль. Использование приведения, как я, позволит избежать нулевой ошибки.

jdweng 30.06.2024 14:57
Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
0
1
57
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

По запросу он возвращает IEnumerable Client/FileType XElement. Следовательно, вам не нужен дополнительный .Element("Client")/.Element("FileType"), поскольку он будет обрабатываться так, как есть потомок с Client/<FileType>.

Получите значение атрибута: result.Attribute("TargetId").Value.

string xml = @"<?xml version = ""1.0"" encoding = ""UTF-8""?>
            <Targets>
                <FileType TargetId = ""image"">
                    <FileExtension>jpg</FileExtension>
                    <FileExtension>jpeg</FileExtension>
                    <FileExtension>tif</FileExtension>
                    <FileExtension>tiff</FileExtension>
                    <FileExtension>gif</FileExtension>
                    <FileExtension>png</FileExtension>
                </FileType>
                <FileType TargetId = ""doc"">
                    <FileExtension>doc</FileExtension>
                </FileType>
            </Targets>";

XDocument doc = XDocument.Parse(xml);
XElement root = doc.Root;

XElement result = (from el in doc.Elements("Targets").Elements("FileType")
    where el.Elements("FileExtension").Any(x => (string)x == "jpg")
    select el).FirstOrDefault();

            
Console.WriteLine(result.Attribute("TargetId").Value);

Альтернативно работа с XPath:

string targetId = root.XPathSelectElement("/Targets/FileType[FileExtension='jpg']").Attribute("TargetId").Value;

То же самое относится и к Клиенту: получите значение атрибута: result2.Attribute("TestAttr").Value.

string xml2 = @"<body>
          <Customers>
            <Client TestAttr = ""testing-123"">
                <Firstname>someguy</Firstname>
                <LastName>some other last name again</LastName>
                <PhoneNumber>12345634543</PhoneNumber>
                <City>some other town</City>
                <State>some other state</State>
            </Client>
          </Customers>
        </body>";
        
XDocument doc2 = XDocument.Parse(xml2);
XElement root2 = doc2.Root;
        
XElement result2 = (from el in doc2.Element("body").Elements("Customers").Elements("Client")
    where el.Elements("PhoneNumber").Any(x => (string)x == "12345634543")
    select el).FirstOrDefault();
        
Console.WriteLine(result2.Attribute("TestAttr").Value);

Альтернативно работа с XPath:

string testAttr = root2.XPathSelectElement("/body/Customers/Client[PhoneNumber=12345634543]").Attribute("TestAttr").Value;
Демо @ .NET Fiddle
Yong Shun 30.06.2024 08:29

Юг - спасибо тебе за это. Получение атрибута сработало. Кажется, моя ошибка заключалась в включении слишком большого количества в этом случае '.Element("Client")'. Однако для "найти" - со списком "FileExtension" это работает только для первого в списке, т.е. сверху, это будет найдите «jpg» или «doc», но не «tiff». Может ли это быть как-то связано с FirstOrDefault? Это фундаментальное различие между моей попыткой (которая вернула сет) и вашей, нацеленной на один результат – что, очевидно, будет лучше, если я смогу понять это лучше. еще раз спасибо

jc508 01.07.2024 03:21

Привет, потому что Element получит первый элемент. Если вы хотите найти какой-либо элемент, вам следует работать с Elements() и Any(). Пожалуйста, проверьте мой последний ответ. Спасибо.

Yong Shun 01.07.2024 04:17

Ён, еще раз спасибо. Ваши предложения работают (конечно), но я думаю, что решения XPath, которые вы предлагаете, гораздо легче понять новичку, такому как я.

jc508 01.07.2024 10:25

Другие вопросы по теме