Упорядоченное / неупорядоченное определение дочерних элементов XML с использованием компактного синтаксиса RELAX NG - PullRequest
0 голосов
/ 05 августа 2010

Я хочу использовать компактный синтаксис RELAX NG для проверки XML-элемента, чьи дочерние элементы составляют один, два, три или n из набора из n определенных элементов.Например, если элементом является «Макет» и существует набор из трех конкретных элементов: «сверху», «по центру» и «снизу», допустимы следующие определения элементов XML:

<Layout>
    <top/>
    <center/>
    <bottom/>
</Layout>

<Layout>
    <top/>
</Layout>

<Layout>
    <center/>
</Layout>

<Layout>
    <bottom/>
</Layout>

<Layout>
    <top/>
    <center/>
</Layout>

Я хочу знать, как написать два шаблона: 1) Разрешить детям быть в любом порядке.2) Ограничьте дочерние элементы в определенном порядке (например, сверху, по центру, снизу).

У меня есть решение для примера XML и упорядоченного шаблона:

element Layout {
   (
      element top { text },
      element center { text }?
   ) |(
      element top { text }?,
      element center { text }?,
      element bottom { text }
   ) |(
      element center { text }
   )
}

У меня нет хорошего решения для более чем 3 элементов и / или для неупорядоченного шаблона.

Ответы [ 3 ]

1 голос
/ 04 мая 2011

Я не уверен, поможет ли это вам, но мы использовали это. Наша проблема: два элемента, в любом порядке, оба могут появляться от 1 до n раз Решение: <interleave> <oneOrMore> <ref name="a.class"/> </oneOrMore> <oneOrMore> <ref name="b.class"/> </oneOrMore> </interleave> Для заказа просто удалите <interleave>. Для случаев от 0 до n используйте <zeroOrMore>.

1 голос
/ 27 ноября 2010

Это обсуждалось недавно в списке RelaxNG (я задал вопрос, как это происходит). Я думаю, что ограничение, которое вы пытаетесь кодифицировать в первом случае, - это что-то вроде строк «разрешить ноль или по одному на каждый набор элементов». Нет аккуратного способа сделать это, но вы можете что-то сделать. Если у вас много возможных дочерних элементов, это, мягко говоря, нелегко (у меня в схеме восемь, и, честно говоря, этого достаточно). Примерно так должно работать в случае 1.

Я использовал именованные шаблоны, чтобы сделать это более разборчивым.

start = e.layout
e.top = element top { text }
e.center = element center { text } 
e.bottom = element bottom { text }
e.layout = element Layout {
    (e.top & e.center? & e.bottom?) |    
    (e.top? & e.center & e.bottom?) |
    (e.top? & e.center? & e.bottom) 
}

Для этого требуется любой из элементов плюс ноль или один из двух других.

Чтобы применить последовательность, вы можете сделать аналогичную вещь, но вместо этого использовать оператор ',', как вы сделали:

start = e.layout
e.top = element top { text }
e.center = element center { text } 
e.bottom = element bottom { text }
e.layout = element Layout {
    (e.top, e.center?) |
    (e.top, e.center, e.bottom?) |        
    (e.top, e.center, e.bottom) |    
    (e.top, e.bottom?)
}

Если бы вы собирались получить что-то более сложное, чем это, я бы серьезно подумал о написании гораздо более простого, который бы просто соответствовал соответствующим элементам, а затем использовал бы правило Schematron для обеспечения подсчета. Итак, для вашего второго требования:

<?xml version="1.0" encoding="UTF-8"?>
<grammar xmlns="http://relaxng.org/ns/structure/1.0" 
         xmlns:sch="http://purl.oclc.org/dsdl/schematron">
  <start>
    <ref name="e.Layout"/>
  </start>
  <define name="e.top">
    <element name="top">
      <text/>
    </element>
  </define>
  <define name="e.center">
    <element name="center">
      <text/>
    </element>
  </define>
  <define name="e.bottom">
    <element name="bottom">
      <text/>
    </element>
  </define>
  <define name="e.Layout">
    <sch:pattern name="check no more than one of each">
      <sch:rule context="Layout/*">
        <sch:assert test="count(../*[local-name(.) eq local-name(current())]) = 1">You may only have one <name/> element as a child of Layout.</sch:assert>
      </sch:rule>
    </sch:pattern>
    <element name="Layout">
      <oneOrMore>
        <group>
          <ref name="e.top"/>
          <ref name="e.center"/>
          <ref name="e.bottom"/>
        </group>
      </oneOrMore>
    </element>
  </define>
</grammar>

или в компактном синтаксисе (в rnc аннотации не очень красивы):

namespace sch = "http://purl.oclc.org/dsdl/schematron"

start = e.Layout
e.top = element top { text }
e.center = element center { text }
e.bottom = element bottom { text }
[
  sch:pattern [
    name = "check no more than one of each"
    "\x{a}" ~
    "      "
    sch:rule [
      context = "Layout/*"
      "\x{a}" ~
      "        "
      sch:assert [
        test = "count(../*[local-name(.) eq local-name(current())]) = 1"
        "You may only have one " rng:name [ ] " element as a child of Layout"
      ]
      "\x{a}" ~
      "      "
    ]
    "\x{a}" ~
    "    "
  ]
]
e.Layout = element Layout { (e.top, e.center, e.bottom)+ }
0 голосов
/ 19 февраля 2011

Вы можете использовать "элемент top {text} ?, center center {text} ?, element bottom {text}?"чтобы получить ноль или один из них в указанном порядке

Или использовать "element top {text}? & element center {text}? & element bottom {text}?"чтобы получить ноль или по одному в каждом порядке.

Оба эти паттерна расширяемы для произвольного числа элементов.

...