【问题标题】:How can I get access to the descendants using Linq?如何使用 Linq 访问后代?
【发布时间】:2020-10-04 23:40:04
【问题描述】:

我有一个如下所示的 .svg 文件:

<g
 inkscape:label="Layer 1"
 inkscape:groupmode="layer"
 id="topLevelGroup">
<rect
   style="fill:#ff00ff;fill-opacity:1.0;stroke:#000000;stroke-width:1;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
   id="rectPurple"
   width="60.0"
   height="30.0"
   x="60.0"
   y="90.0" />
<rect
   style="fill:#ffffff;fill-opacity:1.0;stroke:#000000;stroke-width:2;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
   id="rectWhite"
   width="90.0"
   height="30.0"
   x="30.0"
   y="130.0" />

所以“g”是一个组,并且有多个组而不是一个组,每个组都有一个唯一的 id 属性(这里是 topLevelGroup)。我的任务是编写一个获取组 id 作为参数并返回该组中矩形颜色的方法。出于某种原因,我无法收集这些颜色。这是我尝试过的:

private IEnumerable<XElement> Groups => root.Descendants(ns + "g");
internal IEnumerable<string> GetColorsOfRectsInGroup(string id)
    {
        IEnumerable<XElement> spec_groups = Groups.Where(g => g.Attribute("id").Value.Contains(id))
                                                  .Select(g => g);

        IEnumerable<string> colors = from r in spec_groups.Descendants("rect")
                                     select r.GetFillColor();
        return colors;

    }

所以基本上我首先尝试收集与参数 id 匹配的所有组,然后获取后代“rect”对象及其颜色。我没有收到任何错误消息,它根本没有通过测试。这里也是检查这个的测试方法:

public void GetColorsOfRectsInGroup()
    {
        var colors = s1.GetColorsOfRectsInGroup("group1");
        Assert.True(UnorderedCompareSequences<string>(new string[] { "#ff0000", "#ffff00" },
            colors));

        colors = s2.GetColorsOfRectsInGroup("group2");
        Assert.True(UnorderedCompareSequences<string>(new string[] { "#00ff00", "#0000ff" },
            colors));
    }

我今天刚开始使用 Linq,我一直在浏览网站,但我无法弄清楚。我做错了什么?

【问题讨论】:

  • 您缺少Descendants("rect") 的命名空间,应该是Descendants(ns + "rect")

标签: c# xml linq


【解决方案1】:

您应该在separation of concerns 上工作更多。这对你来说很困难的原因是你试图在一个大声明中做所有事情。

我的建议是将您的任务分成更小的任务。这不仅会让你的任务更容易理解和单元测试/调试,而且,这是一个更大的增强:它将更容易重用你的代码来解决类似的问题,或者调整你的代码以进行细微的更改。

在这种情况下,您应该将您的问题分成两个子问题:

  • 将 SVG 文件读入 Groups 序列,每个序列都有零个或多个 Rectangles
  • 给定 Group 的 ID,给我唯一一个具有此 ID 的组中的矩形颜色。

将其拆分为这些子问题的好处是,您可以将第一种方法重用于需要解释 SVG 文件的其他问题,例如:“给我所有组 ID”,或者给我总表面积组的所有矩形。

如果您的输入不在 SVG 文件中,而是在“带有矩形的组”列表或数据库中,则可以重复使用第二个过程。

最后,很容易看出您可以单独对它们进行单元测试,这使得单元测试边缘条件变得更加容易。

回到你的问题

要读取 SVG 文件,您需要一个类“具有零个或多个矩形的组”:

class Rect
{
    public string Id {get; set;}
    public double Width {get; set;}
    public double Height {get; set;}

    Color GetFillColor();
    ... // etc
}

class Group
{
    public string Id {get; set;}
    ...

    // each group has zero or more Rects:
    public virtual ICollection<Rect> Rects {get; set;}
}

我之所以选择使用 ICollection 而不是 List,是因为恕我直言,我认为 Rect[4] 没有明确的含义。 Rect["rectWhite"] 会,但这超出了您的问题范围。

另一个原因:这个布局看起来很像数据库的输出,就像你从实体框架中得到的一样。如果您以后决定从数据库表中获取您的组,您不必对这个接口进行太多更改。

所以你需要一个程序来将你的 SVG 文件读入Groups 的序列。我对 SVG 文件不是很熟悉,你可能比我知道的更了解如何使用它们。考虑为此使用 NUGET 包。所以我将专注于获取组的颜色。

string groupId = ...
IEnumerable<Group> groups = ... // fetch the groups from the source, in your case a SVG file

var result = groups.Where(group => group.Id == groupId)

    // from the remaining groups, get the FillColors of the Rectangles:
    .SelectMany(group => group.Rects,

        // parameter resultSelector:
        // from every group / rectangle combination, make one new object
        // containing the FillColor of the rectangle (so we don't use the group anymore)
        (group, rectangle) => rectangle.GetFillColor())

    // you might have duplicate colors; If you don't want them, use:
    Distinct();

【讨论】:

  • 谢谢,您的回答非常有帮助!我设法了解这些事情是如何真正起作用的。虽然我没有完全使用你的代码,但在它的帮助下,我成功地让它自己工作了。再次非常感谢!
【解决方案2】:

尝试以下:

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

namespace ConsoleApplication1
{
    class Program
    {
        const string FILENAME = @"c:\temp\test.xml";
        static void Main(string[] args)
        {
            XDocument doc = XDocument.Load(FILENAME);
            XElement root = doc.Root;
            XNamespace ns = root.GetDefaultNamespace();

            string id = "rectPurple";

            List<XElement> Groups = root.Descendants(ns + "g").ToList();

            List<XElement> spec_groups = Groups.Where(x => x.Elements(ns + "rect").Any(y => (string)y.Attribute("id") == id)).ToList(); 
           
        }
    }
}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-12-28
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多