【问题标题】:LINQ to XML: Collapse mutliple levels to single listLINQ to XML:将多个级别折叠到单个列表
【发布时间】:2009-02-02 18:27:08
【问题描述】:

我目前正在开发 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>

因此,各种节点映射到特定属性。这个问题的关键是&lt;EX&gt;&lt;I&gt; 节点。 &lt;EX&gt; 节点将包含一个或多个 &lt;I&gt; 节点的集合,并且在此示例中匹配上述类定义中的“排除”属性。

挑战来了(对我来说)。我无法控制发出此 XML 的 Web 服务,因此无法更改它。您会注意到,在此示例中,一个 &lt;I&gt; 节点还包含一个或多个 &lt;I&gt; 节点的另一个集合。我希望我可以使用 LINQ to XML 查询,它允许我将两个级别合并到一个集合中,并使用一个字符来分隔较低级别的项目,所以在这个例子中,当 LINQ 查询返回一个 TablularEntry对象,它将包含一个排除项的集合,如下所示:

  • 前列腺腺瘤性增生 (600.20-600.21)
  • 前列腺:
  • *腺瘤 (600.20-600.21)
  • *放大 (600.00-600.01)
  • *肥大(600.00-600.01)

因此,在 XML 中,最后 3 个条目实际上是第二个条目的子对象,但在对象的 Excludes 属性中,它们都是同一个集合的一部分,前三个子对象包含标识符字符/字符串。

我有我在下面使用的 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...)

语句来收集子节点,但不能完全完成。当然,这可能是因为我的逻辑有点混乱。

如果您需要更多信息来回答,请随时提问。

提前致谢,

史蒂夫

【问题讨论】:

    标签: c# linq c#-3.0 linq-to-xml


    【解决方案1】:

    试试这个:

        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 个或多个 节点的集合,因此我必须使用 .Elements(复数)语法而不是 .Element(单数)来完成这项工作.仅供参考。
    【解决方案2】:

    后代将为您提供所有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();
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2013-10-24
      • 2014-01-12
      • 1970-01-01
      • 2019-10-12
      • 2015-02-18
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多