首页 > 解决方案 > 根据属性查找 XML 元素

问题描述

假设我有这个 XML

<?xml version="1.0" encoding="utf-8" ?>
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2" mergefield="blah">

    <filter blah="rrr"></filter>
    <filter blah="qqq"></filter>
    <filter blah="www"></filter>

</hibernate-mapping>

如果targetElementsourceElement

namespace Test

{
    class Program
    {
    static void Main(string[] args)
    {

        var path = (@"PATHFORXML");
        XElement target = XElement.Load(path);
        XElement source = XElement.Load(path);

        foreach (var sourceElement in source.Elements())
        {


            XElement targetElement = target.Elements().SingleOrDefault(
                    t =>
                        String.Equals(
                            (string)t.Attribute(GetMergeAttr(target)),
                            (string)sourceElement.Attribute(GetMergeAttr(source)),
                            StringComparison.InvariantCultureIgnoreCase)
                  );
        }


        Console.ReadLine();
    }

    private static string GetMergeAttr(XElement element)
    {
        return (string)element.Attribute("mergefield");
    }
}

}

问题

现在我有一种情况,mergefield可以是"blah"or "blahblah"。所以我希望上面的代码对这个 XML 以同样的方式工作:

<?xml version="1.0" encoding="utf-8" ?>
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2" mergefield="blah,blahblah">

    <filter blah="rrr"></filter>
    <filter blahblah="qqq"></filter>
    <filter blahblah="www"></filter>

</hibernate-mapping>

Aka,任何一个都mergefields应该工作

我的尝试就是这些。GetMergeAttr应该变成这样:

private static string[] GetMergeAttr(XElement element)
        {
            var f = ((string)element.Attribute("mergefield")).Split(',');
            return f;
        }

我无法完成查询部分

XElement targetElement = target.Elements().SingleOrDefault(
                        t =>
                            String.Equals(
                                (string)t.Attribute(GetMergeAttr(target).SingleOrDefault(u=> ?????????)),
                                (string)sourceElement.Attribute(GetMergeAttr(source).SingleOrDefault(u => ?????????))
                                )
                      );

这可行,但是如果我有 3 个以上的合并字段值怎么办(我需要更新此查询以接受任意数量的合并字段,而不仅仅是 2 个)

            XElement targetElement = target.Elements().SingleOrDefault(
                    t =>
                        String.Equals(
                            (string)t.Attribute(GetMergeAttr(target).Last())?? (string)t.Attribute(GetMergeAttr(target).First()),
                            (string)sourceElement.Attribute(GetMergeAttr(source).Last()) ?? (string)sourceElement.Attribute(GetMergeAttr(source).First())
                            )
                  );

标签: c#linq

解决方案


根据评论更新。

我假设以下是您需要的

XElement target = XElement.Parse(txml);
XElement source = XElement.Parse(sxml);
var targetMergeAttrs = GetMergeAttr(target);
var sourceMergeAttrs = GetMergeAttr(source);

foreach (var sourceElement in source.Elements())
{
        foreach(var mergeField in sourceMergeAttrs)
        {

            if(sourceElement.Attributes().Any(x=>x.Name.LocalName.Equals(mergeField)) &&
            target.Elements()
                  .Any(x=>x.Attributes()
                                      .Where(c=>c.Name.LocalName == mergeField 
                                      && targetMergeAttrs.Contains(mergeField)
                                      && c.Value == (string)sourceElement.Attribute(mergeField)).Any()
                                      ))
            {
                XElement targetElement = target.Elements()
                  .FirstOrDefault(x=>x.Attributes()
                                      .Where(c=>c.Name.LocalName == mergeField 
                                      && targetMergeAttrs.Contains(mergeField)
                                      && c.Value == (string)sourceElement.Attribute(mergeField)).Any()
                                      );
                // Do work with targetElement 
                break;
            }

        }
}

推荐阅读