简体   繁体   中英

Find XML element based on attributes

Assume I have this 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>

Each time targetElement should be assigned if its corresponding attribute name is found in sourceElement

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");
    }
}

}

PROBLEM

Now I have a situation where mergefield can be either "blah" or "blahblah" . So I want above code to work same way for this 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, either of mergefields should work

My attempts are those. GetMergeAttr should become this:

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

and I can't finish query part

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

This works, but what if I have more than 3 mergefield values (I need to update this query to accept any number of mergefields, not just 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())
                            )
                  );

Update based on comments.

I assume following is what you require

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;
            }

        }
}

I am not sure what you want to achieve but does this code behave as expected?

using System;
using System.Linq;
using System.Xml.Linq;

namespace ConsoleApp1
{
    class Program
    {
        static void Main(string[] args)
        {
            var path = (@"PATH");
            XElement target = XElement.Load(path);
            XElement source = XElement.Load(path);
            var mergefields = GetMergeAttr(target);

            foreach (var sourceElement in source.Elements())
            {
                var targetElements = from mergefield in mergefields
                                     select target.Elements().SingleOrDefault(t => 
                                         {
                                             if (t.Attribute(mergefield) == null || sourceElement.Attribute(mergefield) == null)
                                             {
                                                 return false;
                                             }

                                             return String.Equals(
                                             t.Attribute(mergefield).Value,
                                             sourceElement.Attribute(mergefield).Value,
                                             StringComparison.InvariantCultureIgnoreCase);
                                         }
                                     );
                foreach (var element in targetElements)
                {
                    Console.WriteLine(element);
                }
            }

            Console.ReadLine();
        }

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

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM