#xml #xslt #xpath #xml-parsing
#xml #xslt #xpath #xml-синтаксический анализ
Вопрос:
добрый вечер.
У меня два вопроса:
1) Как я могу представить логические выражения в XML?
пример:
<service>
<rule> </rule> and <rule> </rule>
<rule> </rule> or <rule> </rule>
</service>
Хорошо, я объясню свою проблему.
Я хочу реализовать механизм вывода.
Проблема заключается в следующем:
сервис имеет набор действий, и каждое действие имеет набор правил.
правила могут быть связаны логическими операторами.
Я думал об использовании XML для представления моих правил. Зная правило X, я хочу извлечь все действия, связанные с этим правилом.
Например
action=admin_required, service=identity, rules=[role:admin or is_admin:1]
action=service_role, service=, rules=[role:service]
action=get_service, service=identity, rules=[admin_required or service_role]
action=list_services, service=identity, rules=[admin_required and admin_service]
action=create_service, service=identity, rules=[admin_required]
action=update_service, service=identity, rules=[admin_required]
с наилучшими пожеланиями
Комментарии:
1. Пожалуйста, размещайте свои вопросы отдельно, поскольку они кажутся не связанными друг с другом. — Подсказка: потратьте немного больше времени и усилий на объяснение того, что вам нужно, особенно. зачем вам это нужно.
2. Чтобы добавить дополнительную информацию, мне изменить вопрос или добавить в качестве ответа?
3. Ответ на ваш вопрос «любым удобным для вас способом». Это действительно зависит от того, что (или кто) находится на принимающей стороне и что им нужно делать с этой информацией. Об этом вы нам практически ничего не сказали, но подойдет любой формат, который позволяет им выполнять свою работу. Это то, что обозначает X (расширяемый) в XML. — P.S. Смотрите также MathML как возможный стандарт.
Ответ №1:
Я считаю, что это выходит за рамки Stack Overflow, но, думаю, я бы сделал:
<service>
<match>all</match>
<rule>...</rule>
<rule>...</rule>
</service>
с match
бытием all
или any
. Или вы могли бы использовать это как атрибут service
.
Не уверен, что понял ваш второй вопрос, не хотите уточнить?
Комментарии:
1. Это неплохая идея, хотя концептуально all / any не является родственным элементом правил, а скорее атрибутом их родительского элемента — так почему бы не сделать это так же и в форме.
2. я изменил свой вопрос. с наилучшими пожеланиями
Ответ №2:
Ну, «лучшего» способа не существует, но одним из способов может быть наличие элемента operator для связи двух правил или, возможно, других операторов. Например, ваше первое правило rules=[role:admin or is_admin:1]
может быть выражено следующим образом
<rules>
<operator type="or">
<rule>role:admin</rule>
<rule>is_admin:1</rule>
</operator>
</rules>
Это также позволило бы использовать «вложенные» операторы. Например rules=[role:admin or (is_admin:1 and role:tester)]
может быть выражено как
<rules>
<operator type="or">
<rule>role:admin</rule>
<operator type="or">
<rule>is_admin:1</rule>
<rule>role:tester</rule>
</operator>
</operator>
</rules>
Вы пометили этот XSLT, поэтому вот некоторый XSLT, который можно использовать для «разбора» правил. Это делается для сопоставления элементов operator, а затем рекурсивно разбирает первый и второй операнды. Затем он выдает результат в зависимости от типа оператора. Элементы правила просто анализируются путем проверки, существует ли правило в параметре, содержащем все правила.
Попробуйте этот XSLT
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" indent="yes"/>
<xsl:param name="rules">role:admin</xsl:param>
<xsl:variable name="checkrule" select="concat(',', $rules, ',')" />
<xsl:template match="actions">
<results>
<xsl:apply-templates />
</results>
</xsl:template>
<xsl:template match="action">
<xsl:variable name="evaluate">
<xsl:apply-templates select="rules/*" mode="rules" />
</xsl:variable>
<xsl:if test="$evaluate=1">
<xsl:value-of select="@*"/>
<xsl:text>;</xsl:text>
</xsl:if>
</xsl:template>
<xsl:template match="operator" mode="rules">
<xsl:variable name="left">
<xsl:apply-templates select="*[1]" mode="rules" />
</xsl:variable>
<xsl:variable name="right">
<xsl:apply-templates select="*[2]" mode="rules" />
</xsl:variable>
<xsl:choose>
<xsl:when test="@type='or'">
<xsl:choose>
<xsl:when test="$left=1 or $right=1">1</xsl:when>
<xsl:otherwise>0</xsl:otherwise>
</xsl:choose>
</xsl:when>
<xsl:when test="@type='and'">
<xsl:choose>
<xsl:when test="$left=1 and $right=1">1</xsl:when>
<xsl:otherwise>0</xsl:otherwise>
</xsl:choose>
</xsl:when>
</xsl:choose>
</xsl:template>
<xsl:template match="rule" mode="rules">
<xsl:choose>
<xsl:when test="contains($rules, .)">1</xsl:when>
<xsl:otherwise>0</xsl:otherwise>
</xsl:choose>
</xsl:template>
</xsl:stylesheet>
При применении к этому XML (представляющему все ваши действия)
<actions>
<action name="admin_required">
<rules>
<operator type="or">
<rule>role:admin</rule>
<rule>is_admin:1</rule>
</operator>
</rules>
</action>
<action name="service_role">
<rules>
<rule>role:service</rule>
</rules>
</action>
<action name="get_service">
<rules>
<operator type="or">
<rule>admin_required</rule>
<rule>admin_service</rule>
</operator>
</rules>
</action>
<action name="list_services">
<rules>
<operator type="and">
<rule>admin_required</rule>
<rule>admin_service</rule>
</operator>
</rules>
</action>
<action name="create_service">
<rules>
<rule>admin_required</rule>
</rules>
</action>
<action name="update_service">
<rules>
<rule>admin_required</rule>
</rules>
</action>
</actions>
Выводится следующее
<results>admin_required;</results>
Если вам нужно «рекурсивное» решение, попробуйте этот XSLT, который использует именованный шаблон, вызываемый рекурсивно с результатами предыдущего вызова.
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" indent="yes"/>
<xsl:param name="rules">role:admin</xsl:param>
<xsl:variable name="checkrule" select="concat(',', $rules, ',')"/>
<xsl:template match="actions">
<results>
<xsl:call-template name="recursive"/>
</results>
</xsl:template>
<xsl:template name="recursive">
<xsl:param name="currentRules" select="$rules"/>
<xsl:param name="currentResults"/>
<xsl:variable name="newResults">
<xsl:apply-templates select="action">
<xsl:with-param name="currentRules" select="$currentRules"/>
</xsl:apply-templates>
</xsl:variable>
<xsl:choose>
<xsl:when test="$newResults != $currentResults">
<xsl:call-template name="recursive">
<xsl:with-param name="currentRules" select="concat($rules, ';', $newResults)"/>
<xsl:with-param name="currentResults" select="$newResults"/>
</xsl:call-template>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="$currentResults"/>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
<xsl:template match="action">
<xsl:param name="currentRules"/>
<xsl:variable name="evaluate">
<xsl:apply-templates select="rules/*" mode="rules">
<xsl:with-param name="currentRules" select="$currentRules"/>
</xsl:apply-templates>
</xsl:variable>
<xsl:if test="$evaluate=1">
<xsl:value-of select="@*"/>
<xsl:text>;</xsl:text>
</xsl:if>
</xsl:template>
<xsl:template match="operator" mode="rules">
<xsl:param name="currentRules"/>
<xsl:variable name="left">
<xsl:apply-templates select="*[1]" mode="rules">
<xsl:with-param name="currentRules" select="$currentRules"/>
</xsl:apply-templates>
</xsl:variable>
<xsl:variable name="right">
<xsl:apply-templates select="*[2]" mode="rules">
<xsl:with-param name="currentRules" select="$currentRules"/>
</xsl:apply-templates>
</xsl:variable>
<xsl:choose>
<xsl:when test="@type='or'">
<xsl:choose>
<xsl:when test="$left=1 or $right=1">1</xsl:when>
<xsl:otherwise>0</xsl:otherwise>
</xsl:choose>
</xsl:when>
<xsl:when test="@type='and'">
<xsl:choose>
<xsl:when test="$left=1 and $right=1">1</xsl:when>
<xsl:otherwise>0</xsl:otherwise>
</xsl:choose>
</xsl:when>
</xsl:choose>
</xsl:template>
<xsl:template match="rule" mode="rules">
<xsl:param name="currentRules"/>
<xsl:choose>
<xsl:when test="contains($currentRules, .)">1</xsl:when>
<xsl:otherwise>0</xsl:otherwise>
</xsl:choose>
</xsl:template>
</xsl:stylesheet>
Это приводит к следующему результату
<results>admin_required;get_service;create_service;update_service;</results>
Комментарии:
1. Как вы видите, роли рекурсивны. как я могу реализовать рекурсию в XSLT? например, если я введу в xsl: param «правила» значение «роль: администратор», xslt должен сказать, что с этим правилом связаны 2 действия: admin_required и list_service с наилучшими пожеланиями
2. На данный момент вам пришлось бы вызывать XSLT несколько раз, причем новые параметры являются результатом предыдущего вызова.
3. так что, нет способа получить все действия с помощью рекурсионного вызова? С наилучшими пожеланиями