2014-01-17 2 views
0

У меня есть вопрос новичков относительно спецификации W3C (обозначение EBNF) выражений XPath. Спецификация можно найти по адресу: http://www.w3.org/TR/xpath/. В частности, у меня возникает вопрос о понимании следующего выражения:синтаксический анализ понимания XPath выражения правил EBNF

(//attribute::name | //attribute::id)[starts-with(string(self::node()), "be") or starts-with(string(self::node()), "1")] 

Это выражение является допустимым. Я проверил, используя http://www.freeformatter.com/xpath-tester.html следующий документ XML:

<documentRoot> 
<!-- Test data --> 
<?xc value="2" ?> 
<parent name="data" > 
    <child id="1" name="alpha" >Some Text</child> 
    <child id="2" name="beta" > 
     <grandchild id="2.1" name="beta-alpha" ></grandchild> 
     <grandchild id="2.2" name="beta-beta" ></grandchild> 
    </child> 
    <pet name="tigger" type="cat" > 
     <data> 
     <birthday month="sept" day="19" ></birthday> 
     <food name="Acme Cat Food" ></food> 
     </data> 
    </pet> 
    <pet name="Fido" type="dog" > 
     <description> 
     Large dog! 
     </description> 
     <data> 
     <birthday month="feb" day="3" ></birthday> 
     <food name="Acme Dog Food" ></food> 
     </data> 
    </pet> 
    <rogue name="is this real?" > 
     <data> 
     Hates dogs! 
     </data> 
    </rogue> 
    <child id="3" name="gamma" mark="yes" > 
     <!-- A comment --> 
     <description> 
     Likes all animals - especially dogs! 
     </description> 
     <grandchild id="3.1" name="gamma-alpha" > 
     <![CDATA[ Some non-parsable character data ]]> 
     </grandchild> 
     <grandchild id="3.2" name="gamma-beta" ></grandchild> 
    </child> 
</parent> 
</documentRoot> 

Это дает мне следующие результаты:

Attribute='id="1"' 
Attribute='name="beta"' 
Attribute='name="beta-alpha"' 
Attribute='name="beta-beta"' 

Это мне не ясно, какая последовательность EBNF производств будет производить выше запрос.

Спасибо за помощь.

ответ

1

Я не знаю, как правильно представлять это, но Expr >>> FilterExpr Predicate:

Expr > OrExpr > AndExpr > EqualityExpr > RelationalExpr > AdditiveExpr > MultiplicativeExpr > UnaryExpr > UnionExpr > PathExpr > FilterExpr > FilterExpr Predicate 

дает вам 2-х частей:

  • фильтра (//attribute::name | //attribute::id)
  • и предикат [starts-with(string(self::node()), "be") or starts-with(string(self::node()), "1")]

(//attribute::name | //attribute::id)

FilterExpr > PrimaryExpr > '(' Expr ')' 
Expr > OrExpr > AndExpr > EqualityExpr > RelationalExpr > AdditiveExpr > MultiplicativeExpr > UnaryExpr > UnionExpr > UnionExpr '|' PathExpr 

дает //attribute::name и //attribute::id

//attribute::name и //attribute::id

PathExpr > LocationPath > AbsoluteLocationPath > AbbreviatedAbsoluteLocationPath > '//' RelativeLocationPath 
RelativeLocationPath > Step > AxisSpecifier NodeTest Predicate* 
    - AxisSpecifier > AxisName '::' 
     - AxisName > 'attribute' 
    - NodeTest > NameTest 

NameTest быть name и id

предиката [starts-with(string(self::node()), "be") or starts-with(string(self::node()), "1")]

Predicate > '[' PredicateExpr ']' > Expr > OrExpr > OrExpr 'or' AndExpr 
    - OrExpr > AndExpr 
    - AndExpr > EqualityExpr > RelationalExpr > AdditiveExpr > MultiplicativeExpr > UnaryExpr > UnionExpr > PathExpr > FilterExpr > PrimaryExpr > FunctionCall > FunctionName '(' (Argument (',' Argument)*)? ')' 
     Argument > Expr 

FunctionName быть starts-with, первый аргумент является еще одним FunctionCall (string функция), второй аргумент является Literal с (через PathExpr > FilterExpr > PrimaryExpr), "быть" и "1".

Наконец, самоуправления :: узел() приходит от:

RelativeLocationPath > Step > AxisSpecifier NodeTest Predicate* 
    - AxisSpecifier > AxisName '::' 
     - AxisName > 'attribute' 
    - NodeTest > NodeType '(' ')' 

NodeType быть 'узел'

+0

Отличный !! Теперь я понимаю. Огромное спасибо. Я полностью пропустил правило FilterExpression. Я застрял с «Step = AxisSpecifier NodeTest Predicate». Очень быстрый и хороший ответ! – user1362700

2

Break-вниз:

 
(      # group 
    //attribute::name  # the long form of //@name 
    |      # union 
    //attribute::id  # the long form of //@id 
)      # group end 
[      # predicate (think "where") 
    starts-with(   # returns true or false 
    string(    #  returns a string 
     self::node()  #  the long form of "." 
    ),     # ) 
    "be"     #  a string literal 
)      # ) 
    or      # logical operator 
    starts-with(   # ...idem 
    string(    # 
     self::node()  # 
    ),     # 
    "1"     # 
)      # 
]      # end predicate 

Таким образом, выражение является довольно излишне многословным версия

(//@name | //@id)[starts-with(., "be") or starts-with(., "1")] 

выбрать все атрибуты с именем "name" или "id", значения которых начинаются с "be" или "1"

Я не уверен, почему вы хотите, чтобы производство EBNF для этого (домашнее задание, я предположим), но понимание самого выражения может помочь вам в этом.

Несколько лишних нот:

  • attribute:: обозначает ось атрибут .
  • Оси могут предшествовать любому node test (ось по умолчанию всегда равна child::).
  • ось self:: является специальной, она содержит только соответствующий узел. Краткая форма self::node() - это точка (.). Подразумевается, что если рассматриваемый узел является узлом <foo>, то self::foo будет соответствовать ему, а self::bar - нет.
  • // является сокращением для /descendant-or-self::node()/
  • string() функции является излишней, поскольку starts-with() преобразует свои аргументы строки неявно в любом случае.
  • Оператор объединения объединяет два набора узлов. Узлы, которые появляются в обоих наборах, не дублируются в результате.
  • Предикаты применяются к каждому узлу в наборе узлов, эффективно фильтруя его.
+1

Чтобы быть педантичным '' // является сокращением для '/ потомок-или-я :: node()/', а не'/descendant :: '(true, во многих случаях это различие не имеет значения, но есть определенные случаи, когда это критично, например, чтобы сделать' // @ foo' корректным, или разница между '// * [1]' и '/ descendant :: * [1]') –

+0

@Ian Спасибо, исправит это! - Чтобы быть еще более педантичным, '//' является сокращением для '/ descendant-or-self ::' - не меньше, но не более того. 'node()' не является частью, если он. :) – Tomalak

+3

Нет, '//' означает точно '/ descendant-or-self :: node() /', включая начальную и конечную косые черты ([XPath spec §2.5] (http://www.w3.org/TR/XPath/# путь-Abbrev)). В противном случае вы не сможете говорить такие вещи, как '// @ foo', поскольку это будет'/descendant :: attribute :: foo' (вы не можете использовать две оси на одном и том же шаге местоположения). –

Смежные вопросы