Выбор 2 элементов из XML

У меня возникли большие проблемы с переводом примеров, которые я нашел для использования с XML.

Начиная с этого (очень урезанная версия всего XML-файла)

<?xml version = "1.0" encoding = "UTF-8"?>
<MetadataSets>
     <MetadataSet MetadataSetID = "999_never" >
         <MetadataSetItem>
             <TagName>NeverSeen</TagName>
             <ItemSeq>10</ItemSeq>
         </MetadataSetItem>
         <MetadataSetItem>
             <TagName>AlsoNeverSeen</TagName>
             <ItemSeq>20</ItemSeq>
         </MetadataSetItem>
    </MetadataSet>
</MetadataSets>

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

'NeverSeen'         10
'AlsoNeverSeen'     20

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

Мне бы хотелось получить помощь, которая помогла бы мне найти правильный путь для решения этой конкретной проблемы, но также есть ли у кого-нибудь предложения по достойному, нетривиальному руководству по LINQ для XML.

Я должен сказать, что у меня есть опыт работы с базами данных; 30 секунд в SQL — 5 дней для XML и LINQ.

Я также путаюсь в разных подходах и, вероятно, непреднамеренно смешиваю их. например те, которые используют «от»

var schema3 = from el in XDoc.Elements("MetadataDomain")
              where (String)el.Attribute("DomainUUID") == pDomainUUID
              select (el);  

затем повторите el, даже если он только один.

Или те, которые не используют «от», а используют «.».

var schema4 = XDoc.Elements("MetadataDomain")
                  .Where(sch => (String)sch.Attribute("DomainUUID") == pDomainUUID)
                  .Select(sch => (String)sch.Element("SchemaName").Value);  

тогда вам все равно придется перебирать результат, даже если он только один.

Я был в восторге от XPath, например

string schema5 = root.XPathSelectElement($"MetadataSets/MetadataDomain[@DomainUUID='{pDomainUUID}']").Element("SchemaName").Value;

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

И как мне выбрать 2 «поля», например, некоторые говорят, что для выбора 2 «полей» используются анонимные типы, например

 select new { car.Make, car.VIN}
 when I try that with XML 
     where (String)el.Attribute("MetadataSetID") == pMetadataSetID
                     select new { el.Element("TagName").Value, el.Element("ItemSeq").Value } ; 

Я получаю ошибку компиляции

Ошибка CS0833: анонимный тип не может иметь несколько свойств с одинаковым именем.

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

Спасибо Джей Си

Как обычно, я выстрелил себе в ногу, показав лишь отрывок информации. Фактически данные нормализуются как минимум в 2 «таблицы», представленные в XML. SchemaName(s), возможно, являются представлениями предметной области. MetadataSetID(ы) указывают подмножество и порядок этих элементов метаданных для конкретной пользовательской аудитории.

Результирующие данные, которые должны быть возвращены вверх по дереву, представляют собой пересечение этих данных для ОДНОГО имени схемы и ОДНОГО MetadataSetID. Как вы можете видеть, даже на этом раннем этапе задействовано множество «столбцов»/свойств. Например, гораздо больше, чем может выдержать словарь.

После вчерашнего разъяснения Юн Шуна я получил дальнейшее развитие, но затем застрял в применении фильтра для MetadataSetID, который был на 2 уровня выше тех тегов, которые я искал. Только что прояснив свои мысли о конечном состоянии, я попробую методы, предложенные Децием.

<!-- semi denormalised - what a database would look like
SchemaName         TagName            TagLabel            TagType     TagSize     TagRepetative   TagDelimiter
EHGS-Standard      FamilyName         Family Name         string       255         true               ;
EHGS-Standard      BusinessName       Business Name       string       255         true               ;
EHGS-Standard      What               What (Categories)   string                   true               ;
EHGS-Standard      Where              Where               string                   true               ;
EHGS-Standard      When               When                string                   true               ;
EHGS-Standard      Contributor        Contributor         string                   true               ;
EHGS-Standard      Comments           Comments            string                   false              ;
EHGS-Standard      Title              Title               string                   true               ;
EHGS-Standard      Copyright          Copyright           string                   true               ;
EHGS-Standard      Subject            Subject             string                   true               ;
EHGS-Standard      Authors            Authors             string                   true               ;
EHGS-Standard      DocumentID         Document ID         string                   false              ;
testPretendSchema  Creator            Creator             string       255         true               ;
testPretendSchema  Reader             Last Reader         string       255         true               ,
testPretendSchema  What               What (whatever)     string                   true               ;

MetadataSetID   TagName     ItemSeq     TagScope    
001_basic       DocumentID      120     Restricted
001_basic       FamilyName      10
001_basic       BusinessName    20
001_basic       What            30
001_basic       Where           40
001_basic       When            50
001_basic       Comments        60
001_basic       Title           70
001_basic       Contributor     80
001_basic       Copyright       90
001_basic       Subject         100
001_basic       Authors         110
999_neverTest   NeverSeen       10
999_neverTest   AlsoNeverSeen   20
  -->

Вы должны указать имя свойства в поле выбора: select new { TagName = el.Element("TagName").Value, ItemSeq = el.Element("ItemSeq").Value }

Yong Shun 06.07.2024 05:49
Стоит ли изучать 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
81
3
Перейти к ответу Данный вопрос помечен как решенный

Ответы 3

Ответ принят как подходящий
  • Я бы предложил создать классы C# из вашего XML (для этого есть онлайн-инструменты).

  • Сериализуйте XML в эти классы

  • Используйте Linq для получения результатов

    [XmlRoot("MetadataSets")]
    public class MetadataSets
    {
        [XmlElement("MetadataSet")]
        public List<MetadataSet> MetadataSetList { get; set; }
    }
    
    public class MetadataSet
    {
        [XmlAttribute("MetadataSetID")]
        public string MetadataSetID { get; set; }
    
        [XmlElement("MetadataSetItem")]
        public List<MetadataSetItem> MetadataSetItems { get; set; }
    }
    
    public class MetadataSetItem
    {
        [XmlElement("TagName")]
        public string TagName { get; set; }
    
        [XmlElement("ItemSeq")]
        public int ItemSeq { get; set; }
    } 
    
    public class Program
    {
        public static void Main()
        {
            var xml = @"<MetadataSets>
                           <MetadataSet MetadataSetID = ""999_never"">
                               <MetadataSetItem>
                                   <TagName>NeverSeen</TagName>
                                   <ItemSeq>10</ItemSeq>
                               </MetadataSetItem>
                               <MetadataSetItem>
                                   <TagName>AlsoNeverSeen</TagName>
                                   <ItemSeq>20</ItemSeq>
                               </MetadataSetItem>
                           </MetadataSet>
                         </MetadataSets>";
    
            var serializer = new XmlSerializer(typeof(MetadataSets));
    
            using var reader = new StringReader(xml);
            var metadataSets = (MetadataSets)serializer.Deserialize(reader);
    
            var items = metadataSets.MetadataSetList.SelectMany(x => x.MetadataSetItems);
    
            foreach(var item in items)
            {
                Console.WriteLine($"{item.TagName} = {item.ItemSeq}");
            }
        }
    }
    

Вот рабочий образец.

Результат:

это оказалось наиболее гибким решением, хотя извлекать данные из вложенных классов по-прежнему сложно. Для всех, кто наткнулся на этот пост, одним из уроков было то, что десериализация не может обрабатывать пустые теги, если только они не являются строковыми типами. т.е. <ItemSeq></ItemSeq> выйдет из строя с крайне бесполезной ошибкой «System.FormatException: входная строка '' была в неправильном формате».

jc508 20.07.2024 01:12

Используйте Xml Linq с SortDictionary:

using System;
using System.Linq;
using System.Collections.Generic;
using System.Data;
using System.Xml;
using System.Xml.Linq;

namespace ConsoleApp10
{

    class Program
    {
        const string FILENAME = @"c:\temp\test.xml";
        static void Main(string[] args)
        {
            XDocument doc = XDocument.Load(FILENAME);
            SortedDictionary<string, string> dict = new SortedDictionary<string,string>( doc.Descendants("MetadataSetItem")
                .GroupBy(x => (string)x.Element("TagName"), y => (string)y.Element("ItemSeq"))
                .ToDictionary(x => x.Key, y => y.FirstOrDefault()));
 

        }
    }
}

Пожалуйста, попробуйте следующее решение.

С#

void Main()
{
    const string FILENAME = @"e:\Temp\jc508.xml";

    XDocument xdoc = XDocument.Load(FILENAME);

    var MetadataSet = xdoc.Descendants("MetadataSetItem")
        .Select(val => new
        {
          TagName = val.Elements("TagName")?.SingleOrDefault().Value,
          ItemSeq = val.Elements("ItemSeq")?.SingleOrDefault().Value,
        }).ToList();

    Console.WriteLine(MetadataSet);
}

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