2

XPath式からドキュメントルートへのすべての参照を抽出し、それらの後にカスタムルートを挿入したいと思います。

スキーマ言語に基づいて作成されたXMLインスタンスドキュメントの検証の小さな部分を実装しています(またはバグを修正しています)。この言語は、自己完結型のXMLチャンクを指定する手段を提供します。このような各チャンクは個別のファイル内で定義され、XML要素の階層を指定します。このような各階層には、XMLドキュメントの非表示のドキュメントルートと同様に、同じドキュメントルートに属する1つ以上のルート要素があります。

ただし、これらのファイルは、それらが指定するものが、より大きなシステムの一部にすぎないという事実を認識していません。この大規模なシステムは、実際には、単一のトップレベルXML要素を持つ別のXMLドキュメント(別のドキュメントルートを持つ)であり、このようなスキーマ言語ファイルの任意の数によって定義されたすべてのルート要素が含まれます。

XML階層内の任意のノードは、検証中に要素が有効であると見なされるためにtrueと評価される必要があるXPath式で制約される場合があります。ここに私の問題の根源があります。これらのXPath式には、システムのドキュメントルートではなく、単一のXMLチャンクのドキュメントルートを参照する絶対ロケーションパスが含まれる場合があります。次のXMLインスタンスについて考えてみます。

<data xmlns="system:uri">
    <root-one xmlns="root-one:uri">
        <items>
            <item>
                <group>base</group>
                <class>person</person>
                <name>John Smith</name>
                <description>valid entry</description>
            </item>
            <item>
                <group>base</group>
                <class>animal</person>
                <name>Dog</name>
                <description>invalid entry</description>
            </item>
        </items>
        <item-classes>
            <item-class>                
                <class>person</class>
                <group>base</group>
            </item-class>
        </item-classes>
    </root-one>
    <root-two xmlns="root-two:uri">
        <!-- obscured content -->
    </root-two>
</data>

{system:uri}dataシステムを表し、 XMLの2つのチャンクで{root-one:uri}root-oneあり{root-two:uri}root-two、それぞれが独自のスキーマ言語ファイル内で定義されています。root-one/items/item各インスタンスは、スキーマ言語ファイル内で定義された次のXPath条件を満たす必要があるとしましょう(インスタンスcurrent()の1つを参照して、XSLTの場合と同じように機能しitemます)。

context: /root-one/items/item
assert: group=/root-one/item-classes/item-class[class=current()/class]/group

これは実際には

context: /data/root-one/items/item
assert: group=/data/root-one/item-classes/item-class[class=current()/class]/group

XPath式のドキュメントルート(/)へのすべての参照を取得し、正しいルートを挿入するにはどうすればよいですか?これらの式がどのように形成されるかを制御することはできないため、XPath 1.0構文を満たしている限り、どのような形やサイズでもかまいませんが、適切に評価する必要があります。

私は現在、これを処理するためにJavaである種のトークナイザーを作成することを考えていますが、より単純な解決策がある場合は、それを使用したくありません。式は、システムドキュメントのコンテキスト内でSchematron XSLT変換中に評価されるため、XSLTを使用してパス修正を何らかの方法で実現できれば、それは完璧です。しかし、私は解決策につながる可能性のあるすべてのポインタを受け入れる準備ができています。

Edit01

これは、XPath式を含むサンプルファイルがどのように見えるかを示しています(頭のてっぺんから)。@test属性の内容を変換したい。属性の値は@context、常に同様の構造を持っているため、変更するのは簡単です。

<?xml version="1.0" encoding="utf-8"?>
<iso:schema    xmlns="http://purl.oclc.org/dsdl/schematron" 
           xmlns:iso="http://purl.oclc.org/dsdl/schematron" 
           xmlns:sch="http://www.ascc.net/xml/schematron"
           xmlns:tl="toplevel:uri"
           xmlns:r1="root-one:uri"
           xmlns:r2="root-two:uri">

  <iso:ns prefix="tl" uri="toplevel:uri" />
  <iso:ns prefix="r1" uri="root-one:uri" />
  <iso:ns prefix="r2" uri="root-two:uri" />

  <iso:pattern>
    <iso:rule context="/r1:root-one/r1:items/r1:item">
      <iso:assert test="r1:group=/r1:root-one/r1:item-classes/r1:item-class[r1:class=current()/r1:class]/r1:group">The group of an item must match one of the predefined class groups.</iso:assert>
    </iso:rule>
  </iso:pattern>

</iso:schema>

@test属性の値は、任意の有効なXPath1.0式である可能性があることに注意してください。式内の任意の場所で定義された任意のドキュメントルート('/')を検索し、カスタムルート要素を挿入できる汎用ソリューションを使用したいと思います。実際のファイルには、任意の数のiso:pattern要素、iso:rule要素などを含めることができます。

Edit02

上記の例では、必要な結果は次のiso:assert要素です。

<iso:assert test="r1:group=/tl:data/r1:root-one/r1:item-classes/r1:item-class[r1:class=current()/r1:class]/r1:group">The group of an item must match one of the predefined class groups.</iso:assert>

Edit03

/ r1:root-one/の前に「/tl:data」を付ける必要があるとどのように判断しますか?ルールを教えてください。– Dimitre Novatchev

/tl:data他の複数のXMLドキュメントを1つに結合することによって作成されるドキュメントのルート要素を表します。これらのドキュメントのコンテンツは、このルート要素に子として追加されます。r1:root-oneそのような子の一人になります。の要素構造がどのr1:root-oneように見えるかを記述するスキーマ定義の一部であるXPath制約は、このサブXMLドキュメントのコンテキストでのみ機能するように設計されています。サブXMLドキュメントが「親」ドキュメントに追加されると、式内に絶対パスが存在する場合、サブXMLドキュメントは意味を失います。したがって、式に含まれている場合、/r1:root-oneこれは新しいドキュメントでは意味がありません(そのroot-one中にルート要素がなくtl:data、唯一のルートです)。そのようなケースをすべて見つけて(/r1:root-one/)、それらを(に変換したい)/tl:data/r1:root-one/)したがって、式は新しいドキュメントのコンテキストで機能します。

正確なルールを指定するのは難しいです。/パスの先頭に表示される(したがって、サブXMLドキュメントのドキュメントルートを参照する) " "は、それぞれ" /tl:data/"に置き換える必要があります。これにより、新しく作成されたドキュメントのドキュメントルートが参照されるようになります。

Edit04

上記のテキストに示されているように、ソリューションは考えられるすべてのXPath式で機能するはずです。追加の例(r1名前空間からの架空の要素が構成されています-これは私の頭の中でより良く聞こえました):

<iso:assert test="r1:group=/r1:root-one/r1:item-classes/r1:item-class[r1:class=current()/r1:class]/r1:group and r1:imaginary-element1=/r1:root-one/r1:item-classes/r1:item-class[r1:class=current()/r1:class]/r1:imaginary-element1" />
<iso:assert test="r1:group=/r1:root-one/r1:item-classes/r1:item-class[r1:class=/r1:root-one/r1:imaginary-constants/r1:imaginary-constant]/r1:group" />

になる必要があります

<iso:assert test="r1:group=/tl:data/r1:root-one/r1:item-classes/r1:item-class[r1:class=current()/r1:class]/r1:group and r1:imaginary-element1=/tl:data/r1:root-one/r1:item-classes/r1:item-class[r1:class=current()/r1:class]/r1:imaginary-element1" />
<iso:assert test="r1:group=/tl:data/r1:root-one/r1:item-classes/r1:item-class[r1:class=/tl:data/r1:root-one/r1:imaginary-constants/r1:imaginary-constant]/r1:group" />

Edit05

XSLT2.0プロセッサに切り替えるオプションがあります。したがって、XSLT2.0ソリューションを受け入れます。

実際、誰かがXPath 1.0式内のドキュメントルートを表す符号と一致するXSLT正規表現を提供できれば、これで問題は解決します(関数を使用します)。私はXPath1.0の文法を調べてきましたが、まだ有用なものは何も付属していません。/replace()

4

2 に答える 2

0

この変換

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
 xmlns:iso="http://purl.oclc.org/dsdl/schematron"
 xmlns:def="http://purl.oclc.org/dsdl/schematron">
 <xsl:output omit-xml-declaration="yes" indent="yes"/>
 <xsl:strip-space elements="*"/>

 <xsl:template match="node()|@*">
     <xsl:copy>
       <xsl:apply-templates select="node()|@*"/>
     </xsl:copy>
 </xsl:template>

 <xsl:template match="iso:assert/@test">
  <xsl:attribute name="test">
    <xsl:value-of select=
    "concat(substring-before(.,'/'),'/t1:data/', substring-after(.,'/'))"/>
  </xsl:attribute>
 </xsl:template>
</xsl:stylesheet>

提供されたXMLドキュメントに適用した場合:

<iso:schema    xmlns="http://purl.oclc.org/dsdl/schematron"
           xmlns:iso="http://purl.oclc.org/dsdl/schematron"
           xmlns:sch="http://www.ascc.net/xml/schematron"
           xmlns:tl="toplevel:uri"
           xmlns:r1="root-one:uri"
           xmlns:r2="root-two:uri">

  <iso:ns prefix="tl" uri="toplevel:uri" />
  <iso:ns prefix="r1" uri="root-one:uri" />
  <iso:ns prefix="r2" uri="root-two:uri" />

  <iso:pattern>
    <iso:rule context="/r1:root-one/r1:items/r1:item">
      <iso:assert test="r1:group=/r1:root-one/r1:item-classes/r1:item-class[r1:class=current()/r1:class]/r1:group">The group of an item must match one of the predefined class groups.</iso:assert>
    </iso:rule>
  </iso:pattern>
</iso:schema>

必要な正しい結果を生成します

<iso:schema xmlns:iso="http://purl.oclc.org/dsdl/schematron"
 xmlns="http://purl.oclc.org/dsdl/schematron"
 xmlns:sch="http://www.ascc.net/xml/schematron"
 xmlns:tl="toplevel:uri" xmlns:r1="root-one:uri"
 xmlns:r2="root-two:uri">
   <iso:ns prefix="tl" uri="toplevel:uri"/>
   <iso:ns prefix="r1" uri="root-one:uri"/>
   <iso:ns prefix="r2" uri="root-two:uri"/>
   <iso:pattern>
      <iso:rule context="/r1:root-one/r1:items/r1:item">
         <iso:assert test="r1:group=/t1:data/r1:root-one/r1:item-classes/r1:item-class[r1:class=current()/r1:class]/r1:group">The group of an item must match one of the predefined class groups.</iso:assert>
      </iso:rule>
   </iso:pattern>
</iso:schema>

説明

の適切な使用:

  1. アイデンティティルール。

  2. テンプレートの一致パターン。

  3. 標準のXPath関数concat()、、substring-before()およびsubstring-after()

于 2013-01-12T15:50:17.107 に答える
0

XPath 1.0文法/仕様を調べて、サポートXSLT2.0を得るために切り替えた後regex、私は次の怪物を思いついた。

<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
 xmlns:iso="http://purl.oclc.org/dsdl/schematron"
 xmlns:def="http://purl.oclc.org/dsdl/schematron">
 <xsl:output omit-xml-declaration="yes" indent="yes"/>
 <xsl:strip-space elements="*"/>

 <xsl:template match="node()|@*">
     <xsl:copy>
       <xsl:apply-templates select="node()|@*"/>
     </xsl:copy>
 </xsl:template>

 <xsl:template match="iso:assert">
    <xsl:copy>

        <xsl:variable name="no-double-slash">
          <!-- need to ignore double slashes since they interfere later -->  
          <xsl:value-of select="replace(@test,'//','unlikelycharseq_double_slash')" />
        </xsl:variable>        
        <xsl:variable name="no-asterisk">
          <!-- need to ignore '*/' which is preceded by and|or|mod|div since that's not mul operator -->  
          <xsl:value-of select="replace($no-double-slash,'([^a-zA-Z0-9_\-](and|or|mod|div)\s*)\*\s*/',concat('$1','unlikelycharseq_asterisk_slash'))" />
        </xsl:variable>
        <xsl:variable name="escaped1">
          <!-- any slash at line start or after an operator (except mul) needs to be replaced, '-' needs to be preceded by whitespace if it represents an operator -->  
          <xsl:value-of select="replace($no-asterisk,'^\s*/|([=+&lt;&gt;\[\(]\s*|\s+\-\s*|[^a-zA-Z0-9_\-](and|or|mod|div)\s*)/',concat('$1','/tl:data/'))" />
        </xsl:variable>
        <xsl:variable name="escaped2">
          <!-- any '*' not preceded by an operator, '@', '::', '(', '[' or ','  is a mul operator, so the following slash needs to be replaced -->  
          <xsl:value-of select="replace($escaped1,'((\s+[^\-]|[^@:\(\[,/|+=&gt;&lt;*])\s*\*\s*)/',concat('$1','/tl:data/'))" />
        </xsl:variable>  
        <xsl:variable name="with-asterisk">
          <!-- restore '*/' which we needed to ignore for the above regexes to work  -->  
          <xsl:value-of select="replace($escaped2,'unlikelycharseq_asterisk_slash','*/')" />
        </xsl:variable>        
        <xsl:variable name="fixed-path">
          <!-- restore '//' which we needed to ignore for the above regexes to work -->  
          <xsl:value-of select="replace($with-asterisk,'unlikelycharseq_double_slash','//')" />
        </xsl:variable>      

      <xsl:attribute name="test">
        <xsl:value-of select="$fixed-path" />
      </xsl:attribute>

      <xsl:comment>
        <xsl:value-of select="@test" />
      </xsl:comment>
  </xsl:copy>
 </xsl:template>
</xsl:stylesheet>

'/'がパス区切り文字ではなくドキュメントルートを表すために満たす必要のあるいくつかのルールを実装します。

  1. 行の先頭に表示されます
  2. オペレーターの後に表示されます
  3. [、(などの後に表示されます。

正規表現と空白を使用した処理も考慮する必要がある場合、いくつかのXPathトークンには特別な処理が必要です。文字「*」および「-」は、演算子をまったく表していない可能性があります。これまでのところ、これはすべてのテストケースで機能しましたが、式を作成するためにXPathの経験ではなく文法に依存しているため、何かを見逃した可能性があります。

これらの正規表現は、XML Schema /XPathregexフレーバーのかなりの数の機能を示しています。一部の高度な機能はサポートされていないため、複数の置換を実行する必要があります。最も注目すべきは見回しです。

誰かが私にこのスパゲッティXSLTよりも良い解決策をくれたら、私は喜んでそれを受け入れます。これが最善ではなく、私の問題に対する唯一の解決策ではない可能性があることに注意してください。

この変換が適用されるとき

<iso:schema    xmlns="http://purl.oclc.org/dsdl/schematron"
           xmlns:iso="http://purl.oclc.org/dsdl/schematron"
           xmlns:sch="http://www.ascc.net/xml/schematron"
           xmlns:tl="toplevel:uri"
           xmlns:r1="root-one:uri"
           xmlns:r2="root-two:uri">

  <iso:ns prefix="tl" uri="toplevel:uri" />
  <iso:ns prefix="r1" uri="root-one:uri" />
  <iso:ns prefix="r2" uri="root-two:uri" />

  <iso:pattern>
    <iso:rule context="/r1:root-one/r1:items/r1:item">
      <iso:assert test="r1:group=/r1:root-one/r1:item-classes/r1:item-class[r1:class=current()/r1:class]/r1:group">The group of an item must match one of the predefined class groups.</iso:assert>
      <iso:assert test="r1:group=/r1:root-one/r1:item-classes/r1:item-class[r1:class=current()/r1:class]/r1:group and r1:imaginary-element1=/r1:root-one/r1:item-classes/r1:item-class[r1:class=current()/r1:class]/r1:imaginary-element1" />
      <iso:assert test="r1:group=/r1:root-one/r1:item-classes/r1:item-class[r1:class=/r1:root-one/r1:imaginary-constants/r1:imaginary-constant]/r1:group" />
      <iso:assert test="../type[. * /root-one/imaginary-constants/imaginary-constant1 > 10]"/>
      <iso:assert test="../type[. mod /root-one/imaginary-constants/imaginary-constant1 = 1]"/>
      <iso:assert test="   /root-one/imaginary-constants/imaginary-constant1"/>
      <iso:assert test="../type[. mod /root-one/imaginary-constants/imaginary-constant1 = 1]"/>
      <iso:assert test="../group= /root-one/item-classes/item-class[name=current()/name]/group and ../class=/root-one/item-classes/item-class[name=current()/name]/class"/>
      <iso:assert test="../type[. = /root-one/imaginary-constants/imaginary-constant1]"/>
      <iso:assert test="../type[.*/root-one/imaginary-constants/imaginary-constant1 = 1]"/>
      <iso:assert test="../type[/root-one/imaginary-constants/imaginary-constant1 mod ../../*/type]"/>
      <iso:assert test="../type[/root-one/imaginary-constants/imaginary-constant1 mod ../multiminus---/type > 1]"/>
      <iso:assert test="../type[../multiminus---*/root-one/imaginary-constants/imaginary-constant1 > 1]"/>
      <iso:assert test="//container/*/type[. &gt; 5]"/>
    </iso:rule>
  </iso:pattern>
</iso:schema>

その結果、これが発生します(パスは期待どおりに修正されます)

<iso:schema xmlns="http://purl.oclc.org/dsdl/schematron"
            xmlns:iso="http://purl.oclc.org/dsdl/schematron"
            xmlns:sch="http://www.ascc.net/xml/schematron"
            xmlns:tl="toplevel:uri"
            xmlns:r1="root-one:uri"
            xmlns:r2="root-two:uri">
   <iso:ns prefix="tl" uri="toplevel:uri"/>
   <iso:ns prefix="r1" uri="root-one:uri"/>
   <iso:ns prefix="r2" uri="root-two:uri"/>
   <iso:pattern>
      <iso:rule context="/r1:root-one/r1:items/r1:item">
         <iso:assert test="r1:group=/tl:data/r1:root-one/r1:item-classes/r1:item-class[r1:class=current()/r1:class]/r1:group"><!--r1:group=/r1:root-one/r1:item-classes/r1:item-class[r1:class=current()/r1:class]/r1:group--></iso:assert>
         <iso:assert test="r1:group=/tl:data/r1:root-one/r1:item-classes/r1:item-class[r1:class=current()/r1:class]/r1:group and r1:imaginary-element1=/tl:data/r1:root-one/r1:item-classes/r1:item-class[r1:class=current()/r1:class]/r1:imaginary-element1"><!--r1:group=/r1:root-one/r1:item-classes/r1:item-class[r1:class=current()/r1:class]/r1:group and r1:imaginary-element1=/r1:root-one/r1:item-classes/r1:item-class[r1:class=current()/r1:class]/r1:imaginary-element1--></iso:assert>
         <iso:assert test="r1:group=/tl:data/r1:root-one/r1:item-classes/r1:item-class[r1:class=/tl:data/r1:root-one/r1:imaginary-constants/r1:imaginary-constant]/r1:group"><!--r1:group=/r1:root-one/r1:item-classes/r1:item-class[r1:class=/r1:root-one/r1:imaginary-constants/r1:imaginary-constant]/r1:group--></iso:assert>
         <iso:assert test="../type[. * /tl:data/root-one/imaginary-constants/imaginary-constant1 &gt; 10]"><!--../type[. * /root-one/imaginary-constants/imaginary-constant1 > 10]--></iso:assert>
         <iso:assert test="../type[. mod /tl:data/root-one/imaginary-constants/imaginary-constant1 = 1]"><!--../type[. mod /root-one/imaginary-constants/imaginary-constant1 = 1]--></iso:assert>
         <iso:assert test="/tl:data/root-one/imaginary-constants/imaginary-constant1"><!--   /root-one/imaginary-constants/imaginary-constant1--></iso:assert>
         <iso:assert test="../type[. mod /tl:data/root-one/imaginary-constants/imaginary-constant1 = 1]"><!--../type[. mod /root-one/imaginary-constants/imaginary-constant1 = 1]--></iso:assert>
         <iso:assert test="../group= /tl:data/root-one/item-classes/item-class[name=current()/name]/group and ../class=/tl:data/root-one/item-classes/item-class[name=current()/name]/class"><!--../group= /root-one/item-classes/item-class[name=current()/name]/group and ../class=/root-one/item-classes/item-class[name=current()/name]/class--></iso:assert>
         <iso:assert test="../type[. = /tl:data/root-one/imaginary-constants/imaginary-constant1]"><!--../type[. = /root-one/imaginary-constants/imaginary-constant1]--></iso:assert>
         <iso:assert test="../type[.*/tl:data/root-one/imaginary-constants/imaginary-constant1 = 1]"><!--../type[.*/root-one/imaginary-constants/imaginary-constant1 = 1]--></iso:assert>
         <iso:assert test="../type[/tl:data/root-one/imaginary-constants/imaginary-constant1 mod ../../*/type]"><!--../type[/root-one/imaginary-constants/imaginary-constant1 mod ../../*/type]--></iso:assert>
         <iso:assert test="../type[/tl:data/root-one/imaginary-constants/imaginary-constant1 mod ../multiminus---/type &gt; 1]"><!--../type[/root-one/imaginary-constants/imaginary-constant1 mod ../multiminus- - -/type > 1]--></iso:assert>
         <iso:assert test="../type[../multiminus---*/tl:data/root-one/imaginary-constants/imaginary-constant1 &gt; 1]"><!--../type[../multiminus- - -*/root-one/imaginary-constants/imaginary-constant1 > 1]--></iso:assert>
         <iso:assert test="//container/*/type[. &gt; 5]"><!--//container/*/type[. > 5]--></iso:assert>
      </iso:rule>
   </iso:pattern>
</iso:schema>
于 2013-01-16T10:41:45.937 に答える