解析XPath表达式

我正在尝试为XPath创建一个’AET’(抽象表达式树)(因为我正在编写一个WYSIWYG XSL编辑器)。 在过去的三到四个小时里,我一直用XPath BNF撞墙。

我想到了另一个解决方案。 我以为我可以编写一个实现IXPathNavigable的类,它在调用CreateNavigator时返回我自己的XPathNavigator。 这个XPathNavigator总是会在任何方法调用上成功,并会跟踪这些调用 – 例如我们移动到客户节点,然后移动到客户节点。 然后我可以使用这些信息(希望)来创建’AET’(因此我们现在可以在对象模型中拥有客户/客户)。

唯一的问题是:我如何通过XPathExpression运行IXPathNavigable?

我知道这太懒了。 但是有没有其他人经历过努力并编写了一个XPath表达式解析器? 我还没有POC我可能的解决方案,因为我无法测试它(因为我不能对IXPathNavigable运行XPathExpression),所以我甚至不知道我的解决方案是否会工作。

这里有一个antlr xpath语法。 由于它的许可证允许,我在这里复制了整个语法,以避免将来链接腐烂。

grammar xpath; /* XPath 1.0 grammar. Should conform to the official spec at http://www.w3.org/TR/1999/REC-xpath-19991116. The grammar rules have been kept as close as possible to those in the spec, but some adjustmewnts were unavoidable. These were mainly removing left recursion (spec seems to be based on LR), and to deal with the double nature of the '*' token (node wildcard and multiplication operator). See also section 3.7 in the spec. These rule changes should make no difference to the strings accepted by the grammar. Written by Jan-Willem van den Broek Version 1.0 Do with this code as you will. */ /* Ported to Antlr4 by Tom Everett  */ main : expr ; locationPath : relativeLocationPath | absoluteLocationPathNoroot ; absoluteLocationPathNoroot : '/' relativeLocationPath | '//' relativeLocationPath ; relativeLocationPath : step (('/'|'//') step)* ; step : axisSpecifier nodeTest predicate* | abbreviatedStep ; axisSpecifier : AxisName '::' | '@'? ; nodeTest: nameTest | NodeType '(' ')' | 'processing-instruction' '(' Literal ')' ; predicate : '[' expr ']' ; abbreviatedStep : '.' | '..' ; expr : orExpr ; primaryExpr : variableReference | '(' expr ')' | Literal | Number | functionCall ; functionCall : functionName '(' ( expr ( ',' expr )* )? ')' ; unionExprNoRoot : pathExprNoRoot ('|' unionExprNoRoot)? | '/' '|' unionExprNoRoot ; pathExprNoRoot : locationPath | filterExpr (('/'|'//') relativeLocationPath)? ; filterExpr : primaryExpr predicate* ; orExpr : andExpr ('or' andExpr)* ; andExpr : equalityExpr ('and' equalityExpr)* ; equalityExpr : relationalExpr (('='|'!=') relationalExpr)* ; relationalExpr : additiveExpr (('<'|'>'|'<='|'>=') additiveExpr)* ; additiveExpr : multiplicativeExpr (('+'|'-') multiplicativeExpr)* ; multiplicativeExpr : unaryExprNoRoot (('*'|'div'|'mod') multiplicativeExpr)? | '/' (('div'|'mod') multiplicativeExpr)? ; unaryExprNoRoot : '-'* unionExprNoRoot ; qName : nCName (':' nCName)? ; functionName : qName // Does not match nodeType, as per spec. ; variableReference : '$' qName ; nameTest: '*' | nCName ':' '*' | qName ; nCName : NCName | AxisName ; NodeType: 'comment' | 'text' | 'processing-instruction' | 'node' ; Number : Digits ('.' Digits?)? | '.' Digits ; fragment Digits : ('0'..'9')+ ; AxisName: 'ancestor' | 'ancestor-or-self' | 'attribute' | 'child' | 'descendant' | 'descendant-or-self' | 'following' | 'following-sibling' | 'namespace' | 'parent' | 'preceding' | 'preceding-sibling' | 'self' ; PATHSEP :'/'; ABRPATH : '//'; LPAR : '('; RPAR : ')'; LBRAC : '['; RBRAC : ']'; MINUS : '-'; PLUS : '+'; DOT : '.'; MUL : '*'; DOTDOT : '..'; AT : '@'; COMMA : ','; PIPE : '|'; LESS : '<'; MORE_ : '>'; LE : '<='; GE : '>='; COLON : ':'; CC : '::'; APOS : '\''; QUOT : '\"'; Literal : '"' ~'"'* '"' | '\'' ~'\''* '\'' ; Whitespace : (' '|'\t'|'\n'|'\r')+ ->skip ; NCName : NCNameStartChar NCNameChar* ; fragment NCNameStartChar : 'A'..'Z' | '_' | 'a'..'z' | '\u00C0'..'\u00D6' | '\u00D8'..'\u00F6' | '\u00F8'..'\u02FF' | '\u0370'..'\u037D' | '\u037F'..'\u1FFF' | '\u200C'..'\u200D' | '\u2070'..'\u218F' | '\u2C00'..'\u2FEF' | '\u3001'..'\uD7FF' | '\uF900'..'\uFDCF' | '\uFDF0'..'\uFFFD' // Unfortunately, java escapes can't handle this conveniently, // as they're limited to 4 hex digits. TODO. // | '\U010000'..'\U0EFFFF' ; fragment NCNameChar : NCNameStartChar | '-' | '.' | '0'..'9' | '\u00B7' | '\u0300'..'\u036F' | '\u203F'..'\u2040' ; 

我编写了XPath解析器和IXPathNavigable的实现(我曾经是XMLPrime的开发人员)。 两者都不容易; 我怀疑IXPathNavigable不会是你希望的廉价胜利,因为不同方法之间的交互有很多细微之处 – 我怀疑一个完整的XPath解析器会更简单(更可靠)。

尽管如此回答你的问题:

 var results xpathNavigable.CreateNavigator().Evaluate("/my/xpath[expression]"). 

您可能需要枚举结果以导致节点被导航。

如果你总是返回true,那么你对以下XPath的所有了解都是它寻找foo的子项: foo[not(bar)]/other/elements

如果你总是返回固定数量的节点,那么你永远不会知道这个XPath a[100]/b/c/大部分a[100]/b/c/

从本质上讲,这是行不通的。