LINQ to XML: сворачивание уровней множества в один список - PullRequest
3 голосов
/ 02 февраля 2009

В настоящее время я работаю над приложением Silverlight, и мне нужно преобразовать данные XML в соответствующие объекты для привязки данных. Базовое определение класса для этого обсуждения:

public class TabularEntry
    {
        public string Tag { get; set; }
        public string Description { get; set; }
        public string Code { get; set; }
        public string UseNote { get; set; }
        public List<string> Excludes { get; set; }
        public List<string> Includes { get; set; }
        public List<string> Synonyms { get; set; }
        public string Flags { get; set; }
        public List<TabularEntry> SubEntries { get; set; }
    }

Ниже приведен пример XML, который может прийти для подачи этого объекта:

<I4 Ref="1">222.2
    <DX>Prostate</DX>
    <EX>
        <I>adenomatous hyperplasia of prostate (600.20-600.21)</I>
        <I>prostatic:
            <I>adenoma (600.20-600.21)</I>
            <I>enlargement (600.00-600.01)</I>
            <I>hypertrophy (600.00-600.01)</I>
        </I>
    </EX>
    <FL>M</FL>
</I4>

Итак, различные узлы сопоставляются с конкретными свойствами. Ключевыми для этого вопроса являются узлы <EX> и <I>. Узлы <EX> будут содержать коллекцию из одного или нескольких узлов <I> и в этом примере соответствуют свойству «Исключает» в приведенном выше определении класса.

Вот идет вызов (для меня). У меня нет контроля над веб-службой, которая генерирует этот XML, поэтому изменить его нельзя. Вы заметите, что в этом примере один узел <I> также содержит другую коллекцию из одного или нескольких узлов <I>. Я надеюсь, что смогу использовать запрос LINQ to XML, который позволит мне объединить оба уровня в одну коллекцию и использовать символ, который будет разделять элементы нижнего уровня, поэтому в этом примере, когда запрос LINQ вернул TablularEntry объект, он будет содержать коллекцию исключаемых элементов, которая будет выглядеть следующим образом:

  • аденоматозная гиперплазия предстательной железы (600.20-600.21)
  • простатической:
  • * аденома (600.20-600.21)
  • * увеличение (600,00-600,01)
  • * гипертрофия (600.00-600.01)

Итак, в XML последние 3 записи на самом деле являются дочерними объектами второй записи, но в свойстве Exclude объекта они являются частью одной коллекции, причем прежние дочерние объекты содержат идентификатор / строку идентификатора.

У меня есть начало запроса LINQ, который я использую ниже, я не могу понять, что будет объединять дочерние объекты для меня. Код, как он существует сейчас:

List<TabularEntry> GetTabularEntries(XElement source)
        {
            List<TabularEntry> result;

            result = (from tabularentry in source.Elements()
                         select new TabularEntry()
                         {
                             Tag = tabularentry.Name.ToString(),
                             Description = tabularentry.Element("DX").ToString(),
                             Code = tabularentry.FirstNode.ToString(),
                             UseNote = tabularentry.Element("UN") == null ? null : tabularentry.Element("UN").Value,
                             Excludes = (from i in tabularentry.Element("EX").Elements("I")
                                             select i.Value).ToList()
                         }).ToList();

            return result;
        }

Я думаю, что мне нужно вложить оператор FROM в

Исключает = (из i ...)

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

Если вам нужна дополнительная информация, чтобы ответить, не стесняйтесь спрашивать.

Заранее спасибо,

Steve

Ответы [ 2 ]

2 голосов
/ 02 февраля 2009

Попробуйте это:

    List<TabularEntry> GetTabularEntries(XElement source)
    {
        List<TabularEntry> result;

        result = (from tabularentry in source.Elements()
                  select new TabularEntry()
                  {
                      Tag = tabularentry.Name.ToString(),
                      Description = tabularentry.Element("DX").ToString(),
                      Code = tabularentry.FirstNode.ToString(),
                      UseNote = tabularentry.Element("UN") == null ? null : tabularentry.Element("UN").Value,
                      Excludes = (from i in tabularentry.Element("EX").Descendants("I")
                                  select (i.Parent.Name == "I" ? "*" + i.Value : i.Value)).ToList()

                  }).ToList();

        return result;
    }

(редактирование)

Если вам нужен текущий вложенный уровень «I», вы можете сделать что-то вроде:

    List<TabularEntry> GetTabularEntries(XElement source)
    {
        List<TabularEntry> result;

        result = (from tabularentry in source.Elements()
                  select new TabularEntry()
                  {
                      Tag = tabularentry.Name.ToString(),
                      Description = tabularentry.Element("DX").ToString(),
                      Code = tabularentry.FirstNode.ToString(),
                      UseNote = tabularentry.Element("UN") == null ? null : tabularentry.Element("UN").Value,
                      Excludes = (from i in tabularentry.Element("EX").Descendants("I")
                                  select (ElementWithPrefix(i, '*'))).ToList()

                  }).ToList();

        return result;
    }

    string ElementWithPrefix(XElement element, char c)
    {
        string prefix = "";
        for (XElement e = element.Parent; e.Name == "I"; e = e.Parent)
        {
            prefix += c;
        }
        return prefix + ExtractTextValue(element);
    }

    string ExtractTextValue(XElement element)
    {
        if (element.HasElements)
        {
            return element.Value.Split(new[] { '\n' })[0].Trim();
        }
        else
            return element.Value.Trim();
    }

Введите:

<EX>
    <I>adenomatous hyperplasia of prostate (600.20-600.21)</I>
    <I>prostatic:
        <I>adenoma (600.20-600.21)</I>
        <I>enlargement (600.00-600.01)</I>
        <I>hypertrophy (600.00-600.01)
            <I>Bla1</I>
            <I>Bla2
                <I>BlaBla1</I>
            </I>
            <I>Bla3</I>
        </I>
            </I>
</EX>

Результат:

* adenomatous hyperplasia of prostate (600.20-600.21)
* prostatic:
* *adenoma (600.20-600.21)
* *enlargement (600.00-600.01)
* *hypertrophy (600.00-600.01)
* **Bla1
* **Bla2
* ***BlaBla1
* **Bla3
0 голосов
/ 02 февраля 2009

Потомки принесут вам всех I детей. FirstNode поможет отделить значение prostatic: от значений его потомков. В значении prostatic: есть возвращаемый символ, который я удалил с помощью Trim.

XElement x = XElement.Parse(@"
<EX>
  <I>adenomatous hyperplasia of prostate (600.20-600.21)</I>
  <I>prostatic:
    <I>adenoma (600.20-600.21)</I>
    <I>enlargement (600.00-600.01)</I>
    <I>hypertrophy (600.00-600.01)</I>
  </I>
</EX>");
//
List<string> result = x
  .Descendants(@"I")
  .Select(i => i.FirstNode.ToString().Trim())
  .ToList();

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

List<string> result2 = x
  .Descendants(@"I")
  .Select(i =>
    new string(Enumerable.Repeat('*', i.Ancestors(@"I").Count()).ToArray())
    + i.FirstNode.ToString().Trim())
  .ToList();
...