C# 基于属性查找XML元素
假设我有这个XMLC# 基于属性查找XML元素,c#,linq,C#,Linq,假设我有这个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
<?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>
}
问题
现在我遇到了一种情况,mergefield
可以是“blah”
或“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>
我无法完成查询部分
XElement targetElement = target.Elements().SingleOrDefault(
t =>
String.Equals(
(string)t.Attribute(GetMergeAttr(target).SingleOrDefault(u=> ?????????)),
(string)sourceElement.Attribute(GetMergeAttr(source).SingleOrDefault(u => ?????????))
)
);
这是可行的,但是如果我有3个以上的mergefield值(我需要更新此查询以接受任意数量的mergefields,而不仅仅是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())
)
);
我不确定您想要实现什么,但这段代码的行为是否符合预期
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(',');
}
}
}
根据评论进行更新 我想以下是你需要的
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;
}
}
}
合并字段是否总是用逗号分隔?如果是,您可以使用String.Split并返回这两个字段。@zanseb是的,它们被逗号分隔。我无法提出正确的LINQ查询,它不断抛出异常@đěxě,可能是我。我重新组织了一个问题,使其能够运行。您的困难是“提取要查找的值”还是“检查多个值”?这些是可分离的任务。不清楚你在问什么。请编辑有关
目标
、源
和预期输出的问题、更多解释和示例。我上面的示例返回“序列包含多个匹配元素异常”请查看我在文章中的最后一个代码部分。如果我有两个合并字段,这就行了。我希望它能适用于任意数量的合并字段。秩序并不重要matter@EricKlaus看来还有一场比赛。在这种情况下应该怎么做?不应该有多个匹配项。它要么是blah,要么是blahblahThis几乎可以工作,但这里的目标元素也可能有空值。是否可以将targetElement XElement改为var?\这取决于具体情况。如果可以确保始终存在匹配项,则可以调用Single
,而不是SingleOrDefault
。否则,您可以将IEnumerable
强制转换为列表。
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(',');
}
}
}
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;
}
}
}