XSL: есть ли быстрая альтернатива использованию переменной в предикате? - PullRequest
1 голос
/ 03 декабря 2010

Отредактировано в слабой попытке уточнить.Система преобразует XML в другой XML с помощью процессора XSL.

Я делаю некоторые правила XSL для машины, имеющей подключаемые карты.Карты будут предоставлять свои собственные правила XSL главному хосту, что, в свою очередь, объединит их в один файл super-XSL, который будет обработан сам по себе, а также отправлен в веб-браузеры, которые отправили HTTP на компьютер.Браузер используется для установки элементов конфигурации для машины, а XSL предназначен для изменения и / или скрытия, и / или отображения некоторых XML.Чтобы упростить мой запрос, я изобрел небольшой пример.

Когда пользователь настраивает элемент на тип B (на выбор несколько таких, как A, B, C, ...),тогда следующие два пункта также должны быть изменены (в некотором роде).В реальной ситуации есть атрибут «скрытый», установленный в true или false, а также дочерние элементы, которые установлены.Следующие два элемента в реальности должны быть скрыты, а также изменены дочерние элементы.

Когда пользователь меняет элемент с типа B на тип A, тогда мне нужно выяснить, какие другие узлынужно, чтобы их атрибут «скрытый» был установлен в false.Пользователь изменит дочерние элементы так, как считает нужным.

Все это "круговое" и упорядоченное, поэтому, если для узла задан тип B, и это последний узел (без следующих одноуровневых элементов)затем затронутые узлы являются первыми в наборе.(В терминах XPath, если узел [4] имеет тип B, то узлы [1] и [2] должны быть скрыты и изменены).

Итак, для моего примера здесь у меня есть входной XML:

<topline>
  <midline type="A" name="mid1" hidden="false"><source name="off"/></midline>
  <midline type="B" name="mid2" hidden="false"><source name="input 1"/></midline>
  <midline type="A" name="mid3" hidden="false"><source name="off"/></midline>
  <midline type="A" name="mid4" hidden="false"><source name="off"/></midline>
</topline>

и XSL изменит это на:

<topline>
  <midline type="A" name="mid1" hidden="false"><source name="off"/></midline>
  <midline type="B" name="mid2" hidden="false"><source name="input 1"/></midline>
  <midline type="A" name="mid3" hidden="true"><source name="input 1"/></midline>
  <midline type="A" name="mid4" hidden="true"><source name="input 1"/></midline>
</topline>

Теперь, если пользователь передумает и изменит mid2 на тип A:

<topline>
  <midline type="A" name="mid1" hidden="false"><source name="off"/></midline>
  <midline type="A" name="mid2" hidden="false"><source name="input 1"/></midline>
  <midline type="A" name="mid3" hidden="true"><source name="input 1"/></midline>
  <midline type="A" name="mid4" hidden="true"><source name="input 1"/></midline>
</topline>

тогда XSL раскроет циркуляр, следующий за братьями и сестрами середины 2, поэтому результат должен быть:

<topline>
  <midline type="A" name="mid1" hidden="false"><source name="off"/></midline>
  <midline type="A" name="mid2" hidden="false"><source name="input 1"/></midline>
  <midline type="A" name="mid3" hidden="false"><source name="input 1"/></midline>
  <midline type="A" name="mid4" hidden="false"><source name="input 1"/></midline>
</topline>

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

<xsl:for-each select="topline">
  <xsl:for-each select="midline">
    <xsl:variable name="masterPosition" select="position()"/>
    <xsl:choose>
      <xsl:when test="@type='B'>
        hide the next two nodes. This is easy: 
            translate($masterPosition, '1234', '2341')
        works nicely.
      </xsl:when>
      <xsl:otherwise>
          <xsl:variable name="prior1" select="translate(masterPosition, '1234', '4123')"/>
          <xsl:variable name="test1" select="../midline[$prior1]/source[1]/@name='off'"/>
          this second line doesn't work: I only get the first node, always. 
          So instead I have 
          <xsl:variable name="test2">
              <xsl:choice>
                  <xsl:when test="$masterPosition='1'"><xsl:value-of select="../midline[4]/source"/></xsl:when>
                  and so on for the other masterPositions
              </xsl:choice>
          </xsl:variable>
          and this is repeated for a few other variables, one each for each relevant
          prior position and for the fields I need to change. I then use these
          variables to change the XML - there's something in the main machine's
          processing to enable this, I believe it is non-standard, so please ignore: 
          (At least it doesn't run against Xalan).
          <set key={$test2}/@hidden, value="false")/>             
      </xsl:otherwise>
    </xsl:choose>
  <xsl:for-each>
</xsl:for-each>

Есть ли более элегантный способ сделать это, о чем вы можете подумать?Если нет, не волнуйтесь, я верю, что мой хак сработает, а также не должен потреблять слишком много MIPS.

Я не могу использовать <xsl:key>, так как наша система не справляется: у нас есть несколькоXSL-источники, которые объединяются в один, а сценарий сортировки (вне моего контроля) просто не понимает <xsl:key>, поэтому, если есть решение с использованием ключей, я не могу его использовать.

СпасибоРичард

Ответы [ 2 ]

1 голос
/ 03 декабря 2010

Чтобы сделать обратное преобразование, вы хотите это :

, когда это преобразование применяется к следующему (при условии) XML, где пользователь только что изменил type='B' в type='A':

<topline>
    <midline type="A" name="mid1" hidden="false">
        <source name="off"/>
    </midline>
    <midline type="A" name="mid2" hidden="false">
        <source name="input 1"/>
    </midline>
    <midline type="A" name="mid3" hidden="true">
        <source name="input 1"/>
    </midline>
    <midline type="A" name="mid4" hidden="true">
        <source name="input 1"/>
    </midline>
</topline>

требуется, получен правильный результат (для следующих двух циклических братьев и сестер типа B установлено значение hidden="false"):

<topline>
   <midline type="A" name="mid1" hidden="false">
      <source name="off"/>
   </midline>
   <midline type="A" name="mid2" hidden="false">
      <source name="input 1"/>
   </midline>
   <midline type="A" name="mid3" hidden="false">
      <source name="input 1"/>
   </midline>
   <midline type="A" name="mid4" hidden="false">
      <source name="input 1"/>
   </midline>
</topline>

с этим исходным XML-файлом (настоящие циклические братья и сестры):

<topline>
    <midline type="A" name="mid1" hidden="true">
        <source name="off"/>
    </midline>
    <midline type="A" name="mid2" hidden="false">
        <source name="input 1"/>
    </midline>
    <midline type="A" name="mid3" hidden="false">
        <source name="input 1"/>
    </midline>
    <midline type="A" name="mid4" hidden="true">
        <source name="input 1"/>
    </midline>
</topline>

снова желаемый, правильный результат .

Примечание : Использование XPath mod оператор для определения положения круговых братьев и сестер.

1 голос
/ 03 декабря 2010

Вот один способ сделать то, что вы хотите, без ключей, на основе сопоставления с шаблоном:

<!-- start with the identity transform -->
<xsl:template match="node()|@*">
  <xsl:copy>
    <xsl:apply-templates select="node()|@*" />
  </xsl:copy>
</xsl:template>

<!-- <midline>s that have two preceding siblings -->
<xsl:template match="midline[@type='A'][count(preceding-sibling::midline) &gt; 1]">
  <xsl:apply-templates select="." mode="source">
    <xsl:with-param name="circular" select="
      preceding-sibling::midline[position() &lt;= 2]
    " />
  </xsl:apply-templates>
</xsl:template>

<!-- <midline>s that have one preceding sibling -->
<xsl:template match="midline[@type='A'][count(preceding-sibling::midline) = 1]">
  <xsl:apply-templates select="." mode="source">
    <xsl:with-param name="circular" select="
      (preceding-sibling::midline|../midline[last()])
    " />
  </xsl:apply-templates>
</xsl:template>

<!-- <midline>s that have no preceding sibling -->
<xsl:template match="midline[@type='A'][count(preceding-sibling::midline) = 0]">
  <xsl:apply-templates select="." mode="source">
    <xsl:with-param name="circular" select="
      ../midline[position() &gt; last() - 2]
    " />
  </xsl:apply-templates>
</xsl:template>

<!-- this template creates a new node with the wanted source -->
<xsl:template match="midline" mode="source">
  <xsl:param name="circular" select="." />
  <xsl:variable name="prevB" select="$circular[@type = 'B' and not(@mode = off)]" />
  <xsl:copy>
    <xsl:copy-of select="@*" />
    <xsl:attribute name="hidden">
      <xsl:value-of select="boolean($prevB)" />
    </xsl:attribute>
    <xsl:choose>
      <xsl:when test="$prevB">
        <xsl:copy-of select="$prevB/source" />
      </xsl:when>
      <xsl:otherwise>
        <xsl:copy-of select="source" />
      </xsl:otherwise>
    </xsl:choose>
  </xsl:copy>
</xsl:template>

Для вашего примера ввода, вывод будет:

<topline>
  <midline type="A" name="mid1" hidden="false"><source name="off"/></midline>
  <midline type="B" name="mid2" hidden="false"><source name="input 1"/></midline>
  <midline type="A" name="mid3" hidden="true"><source name="input 1"/></midline>
  <midline type="A" name="mid4" hidden="true"><source name="input 1"/></midline>
</topline>

Обратите внимание, что ваше описание противоречит вашему примеру выходных данных - это то, что вы получите, если будете строго следовать вашему описанию, если я вас не правильно понял. <xsl:with-param> используется для передачи двух узлов, которые являются «круговыми предшествующими братьями и сестрами» (или пустым набором узлов в случае @type=B' узлов).

Шаблон <xsl:template match="midline" mode="source"> использует этот параметр: если он там есть, он проверяет ваше состояние (т. Е. Один из них @type='B', а не @mode='off').

Если такой узел существует, он копирует его <source>, иначе копирует оригинал <source>.

...