XSLTпроблема - PullRequest
       8

XSLTпроблема

6 голосов
/ 03 мая 2011

У меня есть (очень превосходная электронная таблица, например ...) XML-данные, из которых я должен создать более читабельные.У меня есть заголовки в верхней части структуры, и я хотел бы создать elements из их текстового значения и применить их к остальной части документа.

Вероятно, фактические данные говорят яснее, поэтому мой входной документвыглядит как

<?xml version="1.0"?>
<root>
    <headers>
        <header>line</header>
        <header>product</header>
        <header>order</header>
        <header>qty</header>
        <header>deadline</header>
    </headers>
    <row>
        <data>2</data>
        <data>HU12_SETUP</data>
        <data>16069061</data>
        <data>1</data>
        <data>2011-04-13T09:22:59.980</data>
    </row>
    <row>
        <data>1</data>
        <data>40PFL7605H/12</data>
        <data>16310360</data>
        <data>200</data>
        <data>2011-04-13T09:22:59.980</data>
    </row>
</root>

, и моя цель состоит в том, чтобы иметь XML-документ как:

<?xml version="1.0"?>
<morning>
    <row>
        <line>2</line>
        <product>HU12_SETUP</product>
        <order>16069061</order>
        <qty>1</qty>
        <deadline>0</deadline>
    </row>
    <row>
        <line>1</line>
        <product>40PFL7605H/12</product>
        <order>16310360</order>
        <qty>200</qty>
        <deadline>77</deadline>
    </row>
</morning>

Я хотел бы сделать это "надлежащим" / "эффективным" способом, этопочему я обращаюсь к вам, ребята, чтобы помочь мне.Я подумал, что использование key для сопоставления data позиций с header позициями будет моим решением, но по какой-то причине это просто не работает (я уже ~ X ().

ЧтоМне нужно указать, что не так с моим xsl, и / или если что-то не так с концепцией key, посоветуйте мне лучшее решение.

Вот мой (отладочный) xsl:

<?xml version="1.0"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    <xsl:output method="xml" encoding="UTF-8" indent="yes"/>
    <xsl:strip-space elements="*"/>
    <xsl:key name="header" match="header" use="position()" />

    <xsl:template match="/">
        <morning>
            <xsl:apply-templates />
        </morning>
    </xsl:template>
    <xsl:template match="headers" />
    <xsl:template match="row">
        <xsl:copy>
            <xsl:apply-templates />
        </xsl:copy>
    </xsl:template>
    <xsl:template match="data">
        <xsl:element name="{concat('bla-',position())}">
            <xsl:value-of select="key('header',position())" />
        </xsl:element>
    </xsl:template>
</xsl:stylesheet>

, где я проверяю, что position () действительно верна.

Мой вывод зависит от используемой версии таблицы стилей.

output 1.0 :

<?xml version='1.0' encoding='UTF-8' ?>
<morning>
  <row>
    <bla-1>line</bla-1>
    <bla-2/>
    <bla-3/>
    <bla-4/>
    <bla-5/>
  </row>
  <row>
    <bla-1>line</bla-1>
    <bla-2/>
    <bla-3/>
    <bla-4/>
    <bla-5/>
  </row>
</morning>

output 2.0 :

<?xml version='1.0' encoding='UTF-8' ?>
<morning>
  <row>
    <bla-1>line product order qty deadline</bla-1>
    <bla-2/>
    <bla-3/>
    <bla-4/>
    <bla-5/>
  </row>
  <row>
    <bla-1>line product order qty deadline</bla-1>
    <bla-2/>
    <bla-3/>
    <bla-4/>
    <bla-5/>
  </row>
</morning>

Как видите, key('header',position()) дает мне пустую строку во всех случаях, кроме первого (вот почему у меня естьэто значение, а не имя элемента).

Буду признателен за любую помощь, заранее спасибо!

Добавление :Основываясь на ответах @ LarsH и @ Alejandro (все еще прилипших к этой key вещи ...) я придумал:

<xsl:template match="data">
    <xsl:variable name="posn" select="position()" />
    <xsl:element name="{key('header',1)[$posn]}" />
    <xsl:element name="{key('header',1)[position()]}" />
</xsl:template>

Я вижу, что использование ключа со статическим 1 являетсятупой, но мне страшно, почему два полученных в результате элемента оттуда не совпадают?Первое правильно, второе всегда возвращает мне line, соответственно lineproductorderqtydeadline.

Может ли кто-нибудь указать мне правильное направление?

Ответы [ 2 ]

4 голосов
/ 04 мая 2011

Разница в выводе между XSLT 1.0 и 2.0 заключается в том, что в 1.0 xsl:value-of выводит строковое значение первого узла в выбранном наборе узлов, тогда как (IIRC) в 2.0, он выводит объединенныйзначения всех узлов в выбранном наборе узлов с пробелом в качестве разделителя по умолчанию.(Проверьте это, прежде чем верить мне.)

Итак, исходя из вашего вывода, похоже, что значение ключа для каждого элемента header всегда равно 1. Я не уверен, что position() дасткогда используется в атрибуте ключа use (это зависит от контекста в том смысле, который я не искал), так что для меня это правдоподобно.

Вместо использования ключа с position()Я бы попробовал что-то вроде:

 <xsl:key name="header" match="header"
          use="count(preceding-sibling::header) + 1" />

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

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

<xsl:template match="data">
    <xsl:variable name="posn" value="position()" />
    <xsl:element name="{concat('bla-', $posn)}">
        <xsl:value-of select="/root/headers/header[$posn]" />
    </xsl:element>
</xsl:template>

Обычно используются ключидля повышения производительности, но в этом случае кажется неясным, что будет существенное преимущество по сравнению с использованием предиката [$posn].

Добавление:

Ответчтобы ваше «дополнение» выше было достаточно длинным, оно должно быть здесь, а не комментарий.В дополнение:

<xsl:template match="data">
    <xsl:variable name="posn" select="position()" />
    <xsl:element name="{key('header',1)[$posn]}" />
    <xsl:element name="{key('header',1)[position()]}" />

Как указано в моем комментарии, контекст включает в себя как текущий узел , так и текущий список узлов .Для первого position() при выборе переменной текущий список узлов состоит из всех элементов data, которые являются дочерними элементами обрабатываемого элемента row.Таким образом, position() возвращает позицию текущего узла (элемент data) в этом списке.Это значение сохраняется в переменной $posn.

key('header',1) дает список узлов всех элементов header, по причинам, объясненным выше: значение ключа для каждого header всегда равно 1. Итак key('header',1)[$posn] дает n th элемент заголовка, где n - позиция текущего элемента данных среди его родных элементов.

Для второго вызова position(): в предикате контекст определяетсяприменяя предикат индивидуально к каждому узлу в списке узлов, создаваемом выражением XPath до этой точки, с этим списком узлов в качестве списка текущих узлов .Таким образом, в квадратных скобках key('header',1)[...], список узлов контекста - это список узлов, возвращаемый key('header',1).Опять же, это список всех header элементов.Таким образом, для каждого элемента header, position() здесь возвращает позицию этого заголовка среди его родных элементов.

Теперь мы немного глубже ... Предикаты по своей природе булевы, но когда выражение eв предикате является числовым, он рассматривается как сокращение для position() = e.Таким образом, ваше выражение имени второго элемента эквивалентно

    <xsl:element name="{key('header',1)[position() = position()]}" />

и position() = position() всегда верно для любого заданного контекста, поэтому приведенное выше эквивалентно

    <xsl:element name="{key('header',1)}" />

, который является спискомвсе заголовки.

3 голосов
/ 04 мая 2011

С http://www.w3.org/TR/xslt#key

Таким образом, узел x имеет ключ с именем y и значением z , если итолько если существует элемент xsl:key такой, что:

  • x соответствует шаблону, указанному в атрибуте match элемента xsl:key;

  • значение атрибута name элемента xsl:key равно y

  • , когда выражение, указанное в атрибуте use элемента xsl:key, оценивается с x в качестве текущего узла и со списком узлов, содержащим только x в качестве текущего списка узлов, приводящего к объекту u , тогда либо z равно результату преобразования u в строкукак будто при вызове функции string или u является набором узлов, а z равно строковому значению одного или нескольких узлов в u .

Это означает, что атрибут position() in use всегда равен 1.

Эта таблица стилей:

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    <xsl:strip-space elements="*"/>
    <xsl:variable name="vHeaders" select="/root/headers/header"/>
    <xsl:template match="/">
        <morning>
            <xsl:apply-templates />
        </morning>
    </xsl:template>
    <xsl:template match="headers" />
    <xsl:template match="row">
        <xsl:copy>
            <xsl:apply-templates />
        </xsl:copy>
    </xsl:template>
    <xsl:template match="data">
        <xsl:variable name="vPosition" select="position()"/>
        <xsl:element name="{$vHeaders[$vPosition]}">
            <xsl:apply-templates />
        </xsl:element>
    </xsl:template>
</xsl:stylesheet>

Вывод:

<morning>
    <row>
        <line>2</line>
        <product>HU12_SETUP</product>
        <order>16069061</order>
        <qty>1</qty>
        <deadline>2011-04-13T09:22:59.980</deadline>
    </row>
    <row>
        <line>1</line>
        <product>40PFL7605H/12</product>
        <order>16310360</order>
        <qty>200</qty>
        <deadline>2011-04-13T09:22:59.980</deadline>
    </row>
</morning>
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...