C# 获取XElement的XPath?
我在一份文件中找到了一个元素。给定XElement(和XDocument?),是否有一个扩展方法来获取其完整XPath(即绝对值,例如C# 获取XElement的XPath?,c#,xml,xpath,xelement,C#,Xml,Xpath,Xelement,我在一份文件中找到了一个元素。给定XElement(和XDocument?),是否有一个扩展方法来获取其完整XPath(即绝对值,例如/root/item/element/child) 例如,myXElement.GetXPath() 编辑: 好吧,看来我忽略了一些非常重要的事情。哎呀!需要考虑元素的索引。有关建议的更正解决方案,请参阅我的上一个答案。如果您正在寻找.NET本机提供的内容,则答案是否定的。您必须编写自己的扩展方法来实现这一点。可能有多个xpath指向同一元素,因此找到指向节点的最
/root/item/element/child
)
例如,myXElement.GetXPath()
编辑:
好吧,看来我忽略了一些非常重要的事情。哎呀!需要考虑元素的索引。有关建议的更正解决方案,请参阅我的上一个答案。如果您正在寻找.NET本机提供的内容,则答案是否定的。您必须编写自己的扩展方法来实现这一点。可能有多个xpath指向同一元素,因此找到指向节点的最简单xpath并非易事 也就是说,很容易找到节点的xpath。只需升级节点树,直到读取根节点并组合节点名称,就可以得到有效的xpath。我认为“完整xpath”是指一个简单的标记链,因为可能匹配任何元素的xpath数量可能非常大 这里的问题是,构建任何给定的xpath都非常困难,如果不是特别不可能的话,它将可逆地追溯到同一个元素——这是一个条件吗
如果“否”,那么您可能可以通过递归地循环引用当前元素parentNode来构建查询。如果“是”,那么您将考虑通过交叉引用同级集合中的索引位置来扩展它,引用类ID属性(如果存在),如果有通用解决方案,这将非常依赖于您的XSD。这实际上是一个重复的问题。虽然没有将其标记为答案,但该问题中的方法是明确表示XML文档中节点的XPath的唯一方法,该方法在所有情况下都能工作。(它也适用于所有节点类型,而不仅仅是元素。) 如您所见,它生成的XPath既丑陋又抽象。但它解决了许多回答者在这里提出的问题。这里提出的大多数建议都会生成一个XPath,当用于搜索原始文档时,它将生成一组包含目标节点的一个或多个节点。问题在于“或更多”。例如,如果我有一个数据集的XML表示,则特定数据行元素的原始XPath,
/DataSet1/DataTable1
,也会返回数据表中所有其他数据行的元素。如果不知道XML是如何进行格式化的(比如,是否有主键元素?),就无法消除歧义
但是
/node()[1]/node()[4]/node()[11]
,不管发生什么,它只会返回一个节点。扩展方法:
public static class XExtensions
{
/// <summary>
/// Get the absolute XPath to a given XElement
/// (e.g. "/people/person[6]/name[1]/last[1]").
/// </summary>
public static string GetAbsoluteXPath(this XElement element)
{
if (element == null)
{
throw new ArgumentNullException("element");
}
Func<XElement, string> relativeXPath = e =>
{
int index = e.IndexPosition();
string name = e.Name.LocalName;
// If the element is the root, no index is required
return (index == -1) ? "/" + name : string.Format
(
"/{0}[{1}]",
name,
index.ToString()
);
};
var ancestors = from e in element.Ancestors()
select relativeXPath(e);
return string.Concat(ancestors.Reverse().ToArray()) +
relativeXPath(element);
}
/// <summary>
/// Get the index of the given XElement relative to its
/// siblings with identical names. If the given element is
/// the root, -1 is returned.
/// </summary>
/// <param name="element">
/// The element to get the index of.
/// </param>
public static int IndexPosition(this XElement element)
{
if (element == null)
{
throw new ArgumentNullException("element");
}
if (element.Parent == null)
{
return -1;
}
int i = 1; // Indexes for nodes start at 1, not 0
foreach (var sibling in element.Parent.Elements(element.Name))
{
if (sibling == element)
{
return i;
}
i++;
}
throw new InvalidOperationException
("element has been removed from its parent.");
}
}
和样本输出:
/tests/test[1]/date[1]
/tests/test[1]/time[1]/start[1]
/tests/test[1]/time[1]/end[1]
/tests/test[1]/facility[1]/name[1]
/tests/test[1]/facility[1]/website[1]
/tests/test[1]/facility[1]/street[1]
/tests/test[1]/facility[1]/state[1]
/tests/test[1]/facility[1]/city[1]
/tests/test[1]/facility[1]/zip[1]
/tests/test[1]/facility[1]/phone[1]
/tests/test[1]/info[1]
/tests/test[2]/date[1]
/tests/test[2]/time[1]/start[1]
/tests/test[2]/time[1]/end[1]
/tests/test[2]/facility[1]/name[1]
/tests/test[2]/facility[1]/website[1]
/tests/test[2]/facility[1]/street[1]
/tests/test[2]/facility[1]/state[1]
/tests/test[2]/facility[1]/city[1]
/tests/test[2]/facility[1]/zip[1]
/tests/test[2]/facility[1]/phone[1]
/tests/test[2]/info[1]
这应该解决这个问题。没有 我更新了Chris的代码,将名称空间前缀考虑在内。只修改了GetAbsoluteXPath方法
public static class XExtensions
{
/// <summary>
/// Get the absolute XPath to a given XElement, including the namespace.
/// (e.g. "/a:people/b:person[6]/c:name[1]/d:last[1]").
/// </summary>
public static string GetAbsoluteXPath(this XElement element)
{
if (element == null)
{
throw new ArgumentNullException("element");
}
Func<XElement, string> relativeXPath = e =>
{
int index = e.IndexPosition();
var currentNamespace = e.Name.Namespace;
string name;
if (currentNamespace == null)
{
name = e.Name.LocalName;
}
else
{
string namespacePrefix = e.GetPrefixOfNamespace(currentNamespace);
name = namespacePrefix + ":" + e.Name.LocalName;
}
// If the element is the root, no index is required
return (index == -1) ? "/" + name : string.Format
(
"/{0}[{1}]",
name,
index.ToString()
);
};
var ancestors = from e in element.Ancestors()
select relativeXPath(e);
return string.Concat(ancestors.Reverse().ToArray()) +
relativeXPath(element);
}
/// <summary>
/// Get the index of the given XElement relative to its
/// siblings with identical names. If the given element is
/// the root, -1 is returned.
/// </summary>
/// <param name="element">
/// The element to get the index of.
/// </param>
public static int IndexPosition(this XElement element)
{
if (element == null)
{
throw new ArgumentNullException("element");
}
if (element.Parent == null)
{
return -1;
}
int i = 1; // Indexes for nodes start at 1, not 0
foreach (var sibling in element.Parent.Elements(element.Name))
{
if (sibling == element)
{
return i;
}
i++;
}
throw new InvalidOperationException
("element has been removed from its parent.");
}
}
公共静态类XExtensions
{
///
///获取给定XElement(包括命名空间)的绝对XPath。
///(例如“/a:people/b:person[6]/c:name[1]/d:last[1]”)。
///
公共静态字符串GetAbsoluteXPath(此XElement元素)
{
if(元素==null)
{
抛出新的ArgumentNullException(“元素”);
}
Func=h=e=>
{
int index=e.IndexPosition();
var currentNamespace=e.Name.Namespace;
字符串名;
如果(currentNamespace==null)
{
name=e.name.LocalName;
}
其他的
{
字符串namespacePrefix=e.GetPrefixOfNamespace(currentNamespace);
name=namespacePrefix+“:”+e.name.LocalName;
}
//如果元素是根,则不需要索引
返回(索引==-1)?“/”+名称:string.Format
(
"/{0}[{1}]",
名称
index.ToString()
);
};
var祖先=来自元素中的e。祖先()
选择H(e);
返回字符串.Concat(祖先.Reverse().ToArray())+
相对论h(元素);
}
///
///获取给定元素相对于其属性的索引
///具有相同名称的同级。如果给定元素为
///返回根-1。
///
///
///要获取其索引的元素。
///
公共静态int-IndexPosition(此XElement元素)
{
if(元素==null)
{
抛出新的ArgumentNullException(“元素”);
}
if(element.Parent==null)
{
返回-1;
}
int i=1;//节点的索引从1开始,而不是从0开始
foreach(element.Parent.Elements(element.Name)中的var同级)
{
if(同级==元素)
{
返回i;
}
i++;
}
抛出新的InvalidOperationException
(“元素已从其父元素中删除。”);
}
}
作为扩展方法的一部分,我开发了一个扩展方法来生成元素的简单XPath。它与所选答案类似,但除了XElement之外还支持XAttribute、XText、XCData和XComment。
它可以在这里的项目页面上找到:让我分享我对这个类的最新修改。 基本上,如果元素没有同级元素,并且包含带有local-name()运算符的名称空间,则不包括索引。我在名称空间前缀方面遇到了问题
public static class XExtensions
{
/// <summary>
/// Get the absolute XPath to a given XElement, including the namespace.
/// (e.g. "/a:people/b:person[6]/c:name[1]/d:last[1]").
/// </summary>
public static string GetAbsoluteXPath(this XElement element)
{
if (element == null)
{
throw new ArgumentNullException("element");
}
Func<XElement, string> relativeXPath = e =>
{
int index = e.IndexPosition();
var currentNamespace = e.Name.Namespace;
string name;
if (String.IsNullOrEmpty(currentNamespace.ToString()))
{
name = e.Name.LocalName;
}
else
{
name = "*[local-name()='" + e.Name.LocalName + "']";
//string namespacePrefix = e.GetPrefixOfNamespace(currentNamespace);
//name = namespacePrefix + ":" + e.Name.LocalName;
}
// If the element is the root or has no sibling elements, no index is required
return ((index == -1) || (index == -2)) ? "/" + name : string.Format
(
"/{0}[{1}]",
name,
index.ToString()
);
};
var ancestors = from e in element.Ancestors()
select relativeXPath(e);
return string.Concat(ancestors.Reverse().ToArray()) +
relativeXPath(element);
}
/// <summary>
/// Get the index of the given XElement relative to its
/// siblings with identical names. If the given element is
/// the root, -1 is returned or -2 if element has no sibling elements.
/// </summary>
/// <param name="element">
/// The element to get the index of.
/// </param>
public static int IndexPosition(this XElement element)
{
if (element == null)
{
throw new ArgumentNullException("element");
}
if (element.Parent == null)
{
// Element is root
return -1;
}
if (element.Parent.Elements(element.Name).Count() == 1)
{
// Element has no sibling elements
return -2;
}
int i = 1; // Indexes for nodes start at 1, not 0
foreach (var sibling in element.Parent.Elements(element.Name))
{
if (sibling == element)
{
return i;
}
i++;
}
throw new InvalidOperationException
("element has been removed from its parent.");
}
}
公共静态类XExtensions
{
///
///获取给定XElement(包括命名空间)的绝对XPath。
///(例如“/a:people/b:person[6]/c:name[1]/d:last[1]”)。
///
公共静态字符串GetAbsoluteXPath(此XElement元素)
{
if(元素==null)
{
抛出新的ArgumentNullException(“元素”);
}
Func=h=e=
public static class XExtensions
{
/// <summary>
/// Get the absolute XPath to a given XElement, including the namespace.
/// (e.g. "/a:people/b:person[6]/c:name[1]/d:last[1]").
/// </summary>
public static string GetAbsoluteXPath(this XElement element)
{
if (element == null)
{
throw new ArgumentNullException("element");
}
Func<XElement, string> relativeXPath = e =>
{
int index = e.IndexPosition();
var currentNamespace = e.Name.Namespace;
string name;
if (String.IsNullOrEmpty(currentNamespace.ToString()))
{
name = e.Name.LocalName;
}
else
{
name = "*[local-name()='" + e.Name.LocalName + "']";
//string namespacePrefix = e.GetPrefixOfNamespace(currentNamespace);
//name = namespacePrefix + ":" + e.Name.LocalName;
}
// If the element is the root or has no sibling elements, no index is required
return ((index == -1) || (index == -2)) ? "/" + name : string.Format
(
"/{0}[{1}]",
name,
index.ToString()
);
};
var ancestors = from e in element.Ancestors()
select relativeXPath(e);
return string.Concat(ancestors.Reverse().ToArray()) +
relativeXPath(element);
}
/// <summary>
/// Get the index of the given XElement relative to its
/// siblings with identical names. If the given element is
/// the root, -1 is returned or -2 if element has no sibling elements.
/// </summary>
/// <param name="element">
/// The element to get the index of.
/// </param>
public static int IndexPosition(this XElement element)
{
if (element == null)
{
throw new ArgumentNullException("element");
}
if (element.Parent == null)
{
// Element is root
return -1;
}
if (element.Parent.Elements(element.Name).Count() == 1)
{
// Element has no sibling elements
return -2;
}
int i = 1; // Indexes for nodes start at 1, not 0
foreach (var sibling in element.Parent.Elements(element.Name))
{
if (sibling == element)
{
return i;
}
i++;
}
throw new InvalidOperationException
("element has been removed from its parent.");
}
}
public static string GetAbsoluteXPath(XElement element,int xpversion)
{
IEnumerable<XElement> ancestors = element.AncestorsAndSelf();
string xpath = ancestors.Aggregate(new StringBuilder(),
(str, elem) => str.Insert(0, (xpversion > 1 ? ("/*:" + elem.Name.LocalName) : ("/*[local-name(.) = '" + elem.Name.LocalName + "']")) + "[" + (int)(elem.ElementsBeforeSelf().Where(el => el.Name.LocalName == elem.Name.LocalName).Count() + 1) + "]"),
str => str.ToString());
return xpath;
}