2009-02-05 13 views
6

Sto cercando di creare un 'AET' (Abstract Expression Tree) per XPath (mentre sto scrivendo un editor XY WYSIWYG). Ho battuto la testa contro il muro con XPath BNF da tre a quattro ore.Parse XPath Expressions

Ho pensato ad un'altra soluzione. Ho pensato di poter scrivere una classe che implementa IXPathNavigable, che restituisce un XPathNavigator del mio quando viene chiamato CreateNavigator. Questo XPathNavigator avrebbe sempre avuto successo con qualsiasi chiamata di metodo e avrebbe tenuto traccia di tali chiamate - ad es. ci siamo trasferiti al nodo clienti e quindi al nodo cliente. Potrei quindi utilizzare queste informazioni (si spera) per creare "AET" (quindi avremmo clienti/clienti in un modello a oggetti ora).

L'unica domanda è: come su terra eseguo IXPathNavigable tramite un'espressione XPath?

So che questo è eccessivamente pigro. Ma qualcun altro ha superato lo sforzo e ha scritto un parser di espressioni XPath? Non ho ancora sviluppato la mia soluzione possibile, perché non riesco a testarlo (perché non posso eseguire XPathExpression su un IXPathNavigable), quindi non so nemmeno se la mia soluzione funzionerà.

+0

Il testo che inizia da IXPathNavigable è molto confuso. Potresti modificare la domanda e spiegare meglio? –

risposta

2

C'è una grammatica antlr xpath here. Dal momento che la sua licenza lo consente, ho copiato l'intera grammatica qui per evitare il link rot in futuro.

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 <[email protected]> 
*/ 


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' 
    ; 
+1

Link is dead, :( –

+0

@JimCounts Sono sicuro che non lo stai cercando dopo 4 anni, ma ho appena aggiornato la risposta con un link funzionante –

+0

@jwbroek ecco il tuo codice :) – RobAu

2

ho scritto sia un parser XPath e l'implementazione di IXPathNavigable (ho usato per essere uno sviluppatore per XMLPrime). Né è facile; e sospetto che IXPathNavigable non sarà la vittoria economica che speri, dato che c'è molta sottigliezza nelle interazioni tra diversi metodi - sospetto che un parser XPath in piena regola sia più semplice (e più affidabile).

Per rispondere alla tua domanda però:

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

si sarebbe probabilmente necessario enumerare sui risultati a causare il nodo da navigato.

Se sei tornato sempre vero allora tutti sapresti del seguente XPath è che sembra per i bar figli di pippo: foo[not(bar)]/other/elements

Se si restituisce sempre un numero fisso di nodi allora si sarebbe mai sapere su la maggior parte di questo XPath a[100]/b/c/

In sostanza, questo non funzionerà.