Печать списка элементов с атрибутами из XML-документа с использованием XSLT - PullRequest
0 голосов
/ 31 октября 2011

Мне нужно решить довольно странную проблему.Мне нужна таблица стилей XSLT, которая напечатает список элементов и их атрибутов для документа XML с неизвестной структурой.После многих попыток мне удалось создать такую ​​вещь:

<?xml version="1.0" encoding="ISO-8859-1"?>
<xsl:stylesheet version="1.0" 
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">

<xsl:template match="/">
<HTML>
<title></title>
<body>  

<xsl:call-template name="recurs">
<xsl:with-param name="nextnodes" select="child::*" />
</xsl:call-template>

</body>
</HTML>
</xsl:template>

<xsl:template name="recurs">
<xsl:param name="nextnodes" />
<xsl:for-each select="$nextnodes">
<xsl:if test="not(name(current())=name(following::*)) and  not(name(current())=name(following::*/descendant::*)) ">
    Element <b><xsl:value-of select="name(current())" /></b> has attributes <text> </text>
    <xsl:for-each select="@*">
    <xsl:if test="position()=last()">
    <b><xsl:value-of select="name(current())" /><text>.</text></b>
    </xsl:if>
    <xsl:if test="position()!=last()">
        <b><xsl:value-of select="name(current())" /><text>,  </text></b>
    </xsl:if>
    </xsl:for-each>
    <br /><br />
</xsl:if>
<xsl:call-template name="recurs">
    <xsl:with-param name="nextnodes" select="child::*" />
</xsl:call-template>

</xsl:for-each> 
</xsl:template>

</xsl:stylesheet>

Для такого теста, когда элементная книга снова появляется внутри другого элемента, она отлично работает:

<?xml version="1.0" encoding="utf-8"?>
<?xml-stylesheet type="text/xsl" href="task3_4a.xsl"?>
<catalog subnodes="2">

<cities country="England">
<city name="London" region="London" population="10000" />
<city name="New South Wales" region="Wales" population="800000" />

</cities> 

<articles country="USA">
<article name="My lovely country" src="art1.txt" />
<article name="Places to visit" src="art2.txt" />
<article name="Article 3" src="art3.txt" />
</articles>

<books>
<book title="Warhammer">
</book>
<book title="We fought for truth"> 
</book>
</books>

<scientifics  atr = " ">
<book title="Warhammer">
</book> 
</scientifics>
</catalog>

Нокогда я пытаюсь выполнить другой тест с элементом article внутри books, он не может правильно управлять xml:

<?xml version="1.0" encoding="utf-8"?>
<?xml-stylesheet type="text/xsl" href="task3_4a.xsl"?>
<catalog subnodes="2">

<cities country="England">
<city name="London" region="London" population="10000" />
<city name="New South Wales" region="Wales" population="800000" />

</cities> 

<articles country="USA">
<article name="My lovely country" src="art1.txt" />
<article name="Places to visit" src="art2.txt" />
<article name="Article 3" src="art3.txt" />
</articles>

<books>
<book title="Warhammer">
<article name="My lovely country" src="art1.txt" /> 
</book>
<book title="We fought for truth"> 
<article name="My lovely country" src="art1.txt" /> 
</book>
</books>

<scientifics  atr = " ">
<book title="Warhammer">

<article name="My lovely country" src="art1.txt" /> 
</book> 

</scientifics>

</catalog>

Вывод теперь содержит строку «Элемент article имеет атрибуты name, src.»три раза.И я понятия не имею, как это исправить ...

Ответы [ 2 ]

2 голосов
/ 01 ноября 2011

I.Вот очень короткое и простое решение XSLT 2.0 :

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

 <xsl:template match="/">
    <xsl:for-each-group select="//*"
         group-by="string-join((name(), @*/name()), '|')">
      <xsl:sort select="name()"/>

      <p>
        Element <b><xsl:sequence select="name()"/></b>
        has attributes: <xsl:value-of select="@*/name()" separator=", "/>
      </p>
  </xsl:for-each-group>
 </xsl:template>
</xsl:stylesheet>

Когда это преобразование применяется к предоставленному документу XML :

<catalog subnodes="2">
    <cities country="England">
        <city name="London" region="London" population="10000" />
        <city name="New South Wales" region="Wales" population="800000" />
    </cities>
    <articles country="USA">
        <article name="My lovely country" src="art1.txt" />
        <article name="Places to visit" src="art2.txt" />
        <article name="Article 3" src="art3.txt" />
    </articles>
    <books>
        <book title="Warhammer">
            <article name="My lovely country" src="art1.txt" />
        </book>
        <book title="We fought for truth">
            <article name="My lovely country" src="art1.txt" />
        </book>
    </books>
    <scientifics  atr = " ">
        <book title="Warhammer">
            <article name="My lovely country" src="art1.txt" />
        </book>
    </scientifics>
</catalog>

Требуемый, правильный результат выдается :

<p>
        Element <b>article</b>
        has attributes: name, src</p>
<p>
        Element <b>articles</b>
        has attributes: country</p>
<p>
        Element <b>book</b>
        has attributes: title</p>
<p>
        Element <b>books</b>
        has attributes: </p>
<p>
        Element <b>catalog</b>
        has attributes: subnodes</p>
<p>
        Element <b>cities</b>
        has attributes: country</p>
<p>
        Element <b>city</b>
        has attributes: name, region, population</p>
<p>
        Element <b>scientifics</b>
        has attributes: atr</p>

и отображается в браузере как :

Элемент артикул имеет атрибуты: имя, источник

элемент статьи имеет атрибуты: страна

элемент книга имеет атрибуты: название

элемент books имеет атрибуты:

Элемент каталог имеет атрибуты: подузлы

Элемент городов имеет атрибуты: страна

Элемент город имеет атрибуты: имя, регион, население

Элемент scientifics имеет атрибуты: atr

II.Решение XSLT 1.0 (двухпроходное):

<xsl:stylesheet version="1.0"
 xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
 xmlns:ext="http://exslt.org/common" exclude-result-prefixes="ext">
 <xsl:output omit-xml-declaration="yes" indent="yes"/>
 <xsl:strip-space elements="*"/>

 <xsl:key name="kElByNameandAttrs" match="*"
  use="concat(name(), '|', @_____attribs)"/>

 <xsl:variable name="vrtfPass1">
  <xsl:apply-templates/>
 </xsl:variable>

 <xsl:template match="*">
  <xsl:copy>
   <xsl:attribute name="_____attribs">
     <xsl:for-each select="@*">
       <xsl:sort select="name()"/>

       <xsl:value-of select="concat(name(), ' ')"/>
     </xsl:for-each>
   </xsl:attribute>

   <xsl:apply-templates select="*"/>
  </xsl:copy>
 </xsl:template>

 <xsl:template match="/">
   <xsl:apply-templates mode="pass2" select=
   "ext:node-set($vrtfPass1)//*
          [generate-id()
          =
           generate-id(key('kElByNameandAttrs',
                           concat(name(),
                                  '|',
                                  @_____attribs)
                           )
                            [1])
           ]"
   >
    <xsl:sort select="name()"/>
   </xsl:apply-templates>
 </xsl:template>

 <xsl:template match="*" mode="pass2">
  <p>
        Element <b><xsl:value-of select="name()"/></b>
        has attributes: <xsl:value-of select="@_____attribs"/></p>
 </xsl:template>

</xsl:stylesheet>

когда это преобразование XSLT 1.0 применяется к тому же XML-документу (см. Выше), снова получается требуемый, правильный результат :

<p>
        Element <b>article</b>
        has attributes: name src </p>
<p>
        Element <b>articles</b>
        has attributes: country </p>
<p>
        Element <b>book</b>
        has attributes: title </p>
<p>
        Element <b>books</b>
        has attributes: </p>
<p>
        Element <b>catalog</b>
        has attributes: subnodes </p>
<p>
        Element <b>cities</b>
        has attributes: country </p>
<p>
        Element <b>city</b>
        has attributes: name population region </p>
<p>
        Element <b>scientifics</b>
        has attributes: atr </p>
0 голосов
/ 01 ноября 2011

Одна проблема, с которой вы столкнулись, связана с использованием этого XPath

<xsl:if test="not(name(current())=name(following::*)) ...

Использование оси после :: вернет несколько узлов, но с применением name () *Функция 1007 * получит только имя первого из узлов.

Итак, вместо следующей строки в вашем XSLT> ...

<xsl:if test="not(name(current())=name(following::*)) and  not(name(current())=name(following::*/descendant::*)) ">  

Попробуйте заменить его следующимвместо строки ...

<xsl:if test="not(following::*[name() = name(current())])">

т.е. нет следующего узла (на любом уровне иерархии), который не имеет того же имени, что и текущий узел.

Когда высделать это, будет выведено следующее:

<HTML>
<title></title>
<body>
Element <b>catalog</b> has attributes <text></text><b>subnodes<text>.</text></b><br><br>
Element <b>cities</b> has attributes <text></text><b>country<text>.</text></b><br><br>
Element <b>city</b> has attributes <text></text><b>name<text>,  </text></b><b>region<text>,  </text></b><b>population<text>.</text></b><br><br>
Element <b>articles</b> has attributes <text></text><b>country<text>.</text></b><br><br>
Element <b>books</b> has attributes <text></text><br><br>
Element <b>scientifics</b> has attributes <text></text><b>atr<text>.</text></b><br><br>
Element <b>book</b> has attributes <text></text><b>title<text>.</text></b><br><br>
Element <b>article</b> has attributes <text></text><b>name<text>,  </text></b><b>src<text>.</text></b><br><br></body>
</HTML>

Конечно, это не решает проблему сопоставления, когда два элемента с одинаковыми именами имеют разные атрибуты, но это должно решить вашу непосредственную проблему статья появляется более одного раза.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...