Как создать последовательность строк в сгенерированном файле XSLT?

Я пытаюсь написать XSLT «X_start», который создает другой XSLT «X_generated» из XML-файла, содержащего правила. Моя проблема состоит в том, чтобы создать последовательность для сравнения между тем, что я нашел в файле xml, и тем, что я сгенерировал в X_generated. Идея состоит в том, чтобы сделать это сравнение, и если оно не совпадает, я сгенерирую некоторый текст. В своих примерах я сравниваю, действительно ли я могу найти значение, потому что, если я попытаюсь увидеть, находится ли значение за пределами допустимых значений, к сожалению, я всегда преуспею.

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

Любая помощь/подсказка приветствуется, и дайте мне знать, если вы хотите, чтобы я что-то прояснил.

Пример по правилам:

<?xml version = "1.0" encoding = "UTF-8"?>
<rules>
    <rule type = "conditional">
        <xPath>//elemA/@attribute1</xPath>
        <val allow = "A"/>
        <val allow = "B"/>
    </rule>
    <rule type = "conditional">
        <xPath>//elemC/@attribute1</xPath>
        <val allow = "C"/>
        <val allow = "D"/>
    </rule>
    <rule type = "conditional">
        <xPath>//elemB</xPath>
        <val allow = "one"/>
        <val allow = "two"/>
        <val allow = "three"/>
    </rule>
</rules>

Мой текущий черновик XSLT:

<?xml version = "1.0" encoding = "UTF-8"?>
<xsl:stylesheet xmlns:xsl = "http://www.w3.org/1999/XSL/Transform"
    xmlns:xs = "http://www.w3.org/2001/XMLSchema"
    xmlns:math = "http://www.w3.org/2005/xpath-functions/math"
    xmlns:axsl = "http://www.w3.org/1999/XSL/TransformAlias"
    version = "3.0">
    <xsl:output method = "xml" indent = "true" encoding = "UTF-8"/>
    <xsl:mode on-no-match = "shallow-skip"/>
    <xsl:namespace-alias stylesheet-prefix = "axsl" result-prefix = "xsl"/>
    
    <xsl:template match = "/">
        <axsl:stylesheet>
            <xsl:attribute name = "expand-text" select = "'true'"/>
            <xsl:attribute name = "version" select = "'3.0'"/>
            <xsl:apply-templates select = "rules/*"/>
        </axsl:stylesheet>
    </xsl:template>
    
    <xsl:template match = "rule">
        
        <!-- This creates ("A", "B") -->
        <xsl:variable name = "vals_from_for_each" as = "xs:string*">
            <xsl:for-each select = "val/@allow">
                <xsl:value-of select = "."/>
            </xsl:for-each>
        </xsl:variable>
        
        <!-- This also creates ("A", "B") -->
        <xsl:variable name = "vals_from_seq_text" as = "xs:string*">
            <xsl:sequence select = "val/@allow"/>
        </xsl:variable>
        
        
        <axsl:template>            
            <xsl:attribute name = "match" select = "xPath"/>
            
            <!-- Attempt to move the sequence into the generated template -->
            <axsl:variable name = "vals" as = "xs:string*">
                <!-- But this generates "A B", and is therefore not valid for comparison. 
                    I cannot compare a value like ". = A B", it needs to be ". = ('A', 'B')-->
                <xsl:sequence select = "$vals_from_seq_text"/> 
            </axsl:variable>
            <axsl:if test = ". = $vals">
                <axsl:message>Success</axsl:message>
            </axsl:if>
            
            <!-- trying by building a comparison tree (that doesn't work either... -->
            <axsl:variable name = "value_list" as = "element()*">
                <root>
                    <xsl:for-each select = "val/@allow">
                        <val><xsl:value-of select = "."/></val>
                    </xsl:for-each>
                </root>
            </axsl:variable>
            
            <axsl:if test = ". = $value_list/root/val">
                <axsl:message>Found in tree - Success</axsl:message>
            </axsl:if>
        </axsl:template>
    </xsl:template>

Результирующий XSLT:

<?xml version = "1.0" encoding = "UTF-8"?>
<xsl:stylesheet xmlns:math = "http://www.w3.org/2005/xpath-functions/math"
    xmlns:xs = "http://www.w3.org/2001/XMLSchema"
    xmlns:xsl = "http://www.w3.org/1999/XSL/Transform"
    expand-text = "true"
    version = "3.0">
    <xsl:template match = "//elemA/@attribute1">
        <xsl:variable name = "vals" as = "xs:string*">A B</xsl:variable>
        <xsl:if test = ". = $vals">
            <xsl:message>Success</xsl:message>
        </xsl:if>
        <xsl:variable name = "value_list" as = "element()*">
            <root>
                <val>A</val>
                <val>B</val>
            </root>
        </xsl:variable>
        <xsl:if test = ". = $value_list/root/val">
            <xsl:message>Found in tree - Success</xsl:message>
        </xsl:if>
    </xsl:template>
    <xsl:template match = "//elemC/@attribute1">
        <xsl:variable name = "vals" as = "xs:string*">C D</xsl:variable>
        <xsl:if test = ". = $vals">
            <xsl:message>Success</xsl:message>
        </xsl:if>
        <xsl:variable name = "value_list" as = "element()*">
            <root>
                <val>C</val>
                <val>D</val>
            </root>
        </xsl:variable>
        <xsl:if test = ". = $value_list/root/val">
            <xsl:message>Found in tree - Success</xsl:message>
        </xsl:if>
    </xsl:template>
    <xsl:template match = "//elemB">
        <xsl:variable name = "vals" as = "xs:string*">one two three</xsl:variable>
        <xsl:if test = ". = $vals">
            <xsl:message>Success</xsl:message>
        </xsl:if>
        <xsl:variable name = "value_list" as = "element()*">
            <root>
                <val>one</val>
                <val>two</val>
                <val>three</val>
            </root>
        </xsl:variable>
        <xsl:if test = ". = $value_list/root/val">
            <xsl:message>Found in tree - Success</xsl:message>
        </xsl:if>
    </xsl:template>
</xsl:stylesheet>

Таким образом, ни одно из сообщений не срабатывает, мне не удается создать что-то похожее в моем полученном XSLT. Любая созданная последовательность создается как отдельные значения в строке (?), и я не могу выполнить поиск в сгенерированном сегменте дерева (что, как я догадывался, не сработает, но, как бы отчаянно это ни было, я все равно попытался). Во время написания этого я начал думать, что одним из способов может быть использование отдельного режима, в котором я мог бы создать совершенно отдельный XML-документ со значениями поиска, но это кажется немного хуже, чем создание поиска в каждом шаблоне. Итак, какие идеи о том, как я могу перенести допустимые значения из моего исходного XML в сгенерированный XSLT?

Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
0
0
82
4
Перейти к ответу Данный вопрос помечен как решенный

Ответы 4

IIUC, вы НЕ хотите создавать «последовательность строк». Вы хотите создать строку, которая при оценке как выражение создаст последовательность строк.

Рассмотрим что-то вроде:

XSLT 3.0

<xsl:stylesheet version = "3.0" 
xmlns:xsl = "http://www.w3.org/1999/XSL/Transform"
xmlns:axsl = "http://www.w3.org/1999/XSL/TransformAlias">
<xsl:output method = "xml" version = "1.0" encoding = "UTF-8" indent = "yes"/>

<xsl:namespace-alias stylesheet-prefix = "axsl" result-prefix = "xsl"/>

<xsl:template match = "/rules">
    <axsl:stylesheet version = "3.0">
        <xsl:apply-templates/>
    </axsl:stylesheet>
    </xsl:template>

<xsl:template match = "rule">
    <axsl:template match = "{xPath}">            
        <axsl:if test = ". = ({string-join(val/@allow ! concat('''', ., ''''), ',')})">
            <axsl:message>Success</axsl:message>
        </axsl:if>
    </axsl:template>
</xsl:template>

</xsl:stylesheet>

P.S. Обратите внимание, что запуск шаблона соответствия с помощью // ничего не дает.

Это работает, и я понимаю предлагаемое решение. Что касается // в каждом совпадении, они исходят из исходных правил, и я думаю, что они были добавлены, чтобы люди могли использовать их непосредственно в инструменте, который, возможно, анализирует весь документ и применяет выражения в некоторых функциях XPath. Я полагал, что XSLT может каким-то образом решить проблему проверки.

Zug_Bug 21.01.2023 23:00

Один из способов, поскольку у вас есть простые строковые значения, — хранить их в массиве XDM, который вы сериализуете как JSON, и вставляете сериализованные в сгенерированный XSLT:

<?xml version = "1.0" encoding = "UTF-8"?>
<xsl:stylesheet xmlns:xsl = "http://www.w3.org/1999/XSL/Transform"
    xmlns:xs = "http://www.w3.org/2001/XMLSchema"
    xmlns:math = "http://www.w3.org/2005/xpath-functions/math"
    xmlns:axsl = "http://www.w3.org/1999/XSL/TransformAlias"
    version = "3.0">
    <xsl:output method = "xml" indent = "true" encoding = "UTF-8"/>
    <xsl:mode on-no-match = "shallow-skip"/>
    <xsl:namespace-alias stylesheet-prefix = "axsl" result-prefix = "xsl"/>
    
    <xsl:template match = "/">
        <axsl:stylesheet>
            <xsl:attribute name = "expand-text" select = "'true'"/>
            <xsl:attribute name = "version" select = "'3.0'"/>
            <xsl:apply-templates select = "rules/*"/>
        </axsl:stylesheet>
    </xsl:template>
    
    <xsl:template match = "rule">

        
        <!-- This also creates ("A", "B") -->
        <xsl:variable name = "vals_from_seq_text" as = "array(xs:string)" select = "array {val/@allow/string() }"/>

        
        <axsl:template>   
        
            <xsl:attribute name = "match" select = "xPath"/>
            
            <!-- Attempt to move the sequence into the generated template -->
            <axsl:variable name = "vals" as = "array(xs:string)" select = "{serialize($vals_from_seq_text, map{'method':'json'})}"/>

            <axsl:if test = ". = $vals?*">
                <axsl:message>Success</axsl:message>
            </axsl:if>
            

        </axsl:template>
    </xsl:template>
    
</xsl:stylesheet>

Это работает, и я вижу, что результатом является массив. Не могли бы вы указать мне какой-нибудь простой для понимания источник в части map{'method':'json'}? Я действительно хотел бы понять эти инструменты, но, к сожалению, я не получил этого, прочитав спецификацию.

Zug_Bug 21.01.2023 23:06

@Zug_Bug, я полагаю, вы знаете, например. <xsl:output method = "html"/> или <xsl:output method = "text"/> или <xsl:output method = "xml"/>, чтобы объявить метод сериализации для таблицы стилей XSLT 2? Ну, в XSLT 3 есть два дополнительных метода, один с именем json, другой с именем adaptive. Кроме того, это функция XPath 3.1 с именем serialize, которая в качестве первого аргумента принимает любую последовательность XSLT/XDM/XPath, а в качестве второго аргумента — параметры сериализации, но не в форме объявления xsl:output, а в виде «карты» XPath 3.1.

Martin Honnen 21.01.2023 23:12

Так что все, что я сделал, это использовал <xsl:output method = "json"/>, но в синтаксисе XPath 3.1, а не на уровне XSLT. Синтаксис карт XSLT объясняется в спецификации XSLT 3 и спецификации XPath 3.1: w3.org/TR/xpath-31/#id-maps

Martin Honnen 21.01.2023 23:14

Проще всего сгенерировать последовательность строк в виде XML-структуры:

<axsl:variable name = "strings-as-xml" as = "element(s)*">
  <xsl:for-each select = "$vals_from_seq_text">
    <s>{.}</s>
  </xsl:for-each>
</axsl:variable>

<axsl:variable name = "value_list" as = "xs:string*"
               select = "$strings-as-xml!string()"/>

Другое решение, которое, конечно же, работает :-) Как я уже спрашивал в другом заданном ответе, мне интересно, почему моя сконструированная переменная, содержащая элементы, не работает для сравнения? Я могу получить текстовое значение из каждого включенного элемента и запустить для него "string()", но я не могу получить их в последовательность для сравнения, которую я обычно разрешал бы, если бы узел элемента был в документе, который я запускал. XSLT включен? Смущенный...

Zug_Bug 23.01.2023 08:25

Я думал, вы сами ответили на это в комментарии к своему коду: ваш код генерирует <xsl:variable name = "..">A B C</xsl:variable>, который представляет собой одну строку, а не последовательность строк.

Michael Kay 23.01.2023 11:05

Да, это я видел, но я имел в виду переменную $value_list. @ michael.hor257k подумал, что это может сработать, если я удалю тип из переменной. Я попробую это, просто чтобы посмотреть, работает ли это, чтобы я мог понять это немного лучше.

Zug_Bug 23.01.2023 14:17
Ответ принят как подходящий

Ахм... Взяв подсказку из ответа Майкла Кея, почему бы вам не обойти всю проблему "последовательности строк" и сделать просто:

<xsl:template match = "rule">
    <axsl:template match = "{xPath}"> 
        <axsl:variable name = "vals">
            <xsl:copy-of select = "val"/>
        </axsl:variable>     
        <axsl:if test = ". = $vals/val/@allow">
            <axsl:message>Success</axsl:message>
        </axsl:if>
    </axsl:template>
</xsl:template>

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

Zug_Bug 23.01.2023 08:21

Какую переменную вы имеете в виду?

michael.hor257k 23.01.2023 08:39

Я попытался создать переменную с элементами под названием «value_list». Но я предполагаю, что это как-то связано с переменными и контекстом?

Zug_Bug 23.01.2023 09:09

Мне потребуется больше времени, чтобы исследовать это, но я считаю, что ваша попытка использовать переменную $value_list сработает, если вы удалите атрибут as = "element()*".

michael.hor257k 23.01.2023 11:56

Я попытался удалить атрибут as = "element()*". Это тоже работает, и при отладке в Oxygen я вижу, что он создает document-node/<#document/fragment>, как и copy-of.

Zug_Bug 24.01.2023 08:26

Да. В качестве альтернативы вы можете сохранить атрибут as, но тогда вам нужно будет изменить тест с test = ". = $value_list/root/val" на test = ". = $value_list/val" — см.: w3.org/TR/xslt-30/#variable-values ​​.

michael.hor257k 24.01.2023 08:35

Другие вопросы по теме