XForms 重复:相同的元素名称,不同的值约束

Posted

技术标签:

【中文标题】XForms 重复:相同的元素名称,不同的值约束【英文标题】:XForms repeat: same element name, different value constraints 【发布时间】:2015-07-17 00:17:57 【问题描述】:

我正在尝试在 XForms(eXist-db 中的 XSLTForms 实现)中编辑 RDF/XML,并且我需要对 xf:repeat 结构中的同名元素实施不同的值约束。例如,我有一个 bf:subject 元素,它可以将默认 URI 作为其 @rdf:resource 属性的值,也可以将任意 URI 链接到表单中定义的其他资源(为简洁起见,我省略了这些从下面提供的示例中)。

xf:repeat 结构中,如何区分具有相同名称的元素?我可以使用将@rdf:resource 的值限制为xf:model 中指定的默认URI 的谓词来处理第一种情况,但是对于@rdf:resource 可以采用的情况,我找不到实现差异处理的方法任意 URI。

注意:第二个嵌套的 xf:repeat 中没有表单控件,因为 @rdf:resource 的值是使用更新 XForms 实例的单独 JavaScript 库 (jsPlumb) 动态更新的。

<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet href="http://localhost:8080/exist/apps/xsltforms/xsltforms.xsl" type="text/xsl"?>
<?xsltforms-options debug="yes"?>
<?css-conversion no?>
<?xml-model href="http://www.oxygenxml.com/1999/xhtml/xhtml-xforms.nvdl" schematypens="http://purl.oclc.org/dsdl/nvdl/ns/structure/1.0"?>
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:bf="http://bibframe.org/vocab/"
    xmlns:ev="http://www.w3.org/2001/xml-events"
    xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
    xmlns:rdfs="http://www.w3.org/2000/01/rdf-schema#" xmlns:xf="http://www.w3.org/2002/xforms"
    xmlns:xs="http://www.w3.org/2001/XMLSchema">
    <head>
        <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
        <title>Editor</title>
        <!--Model-->
        <xf:model id="rdf-model">
            <xf:instance id="graph">
                <rdf:RDF>
                    <bf:Work rdf:about="">
                        <bf:subject rdf:resource="http://id.loc.gov/vocabulary/geographicAreas/s-ag"></bf:subject>
                        <bf:subject rdf:resource=""/>
                    </bf:Work>
                </rdf:RDF>
            </xf:instance>
            <!-- Template -->
            <xf:instance id="bf-Work-template">
                <rdf:RDF>
                    <bf:Work rdf:about="">
                        <bf:subject rdf:resource="http://id.loc.gov/vocabulary/geographicAreas/s-ag"></bf:subject>
                        <bf:subject rdf:resource=""/>
                    </bf:Work>
                </rdf:RDF>
            </xf:instance>
        </xf:model>
    </head>
    <body>
        <div id="header">
            <h1>Editor</h1>
        </div>
        <div id="forms">
            <!-- Repeat for Work entity -->
            <xf:repeat nodeset="instance('graph')/bf:Work" id="repeat-Work-graph">

                <!-- Repeat bf:subject elements that have a default value. -->
                <xf:repeat
                    nodeset="bf:subject[@rdf:resource[. = 'http://id.loc.gov/vocabulary/geographicAreas/s-ag']]">
                    <div style="border:solid black 1px;">
                        <xf:input
                            ref="@rdf:resource[. = 'http://id.loc.gov/vocabulary/geographicAreas/s-ag']">
                            <xf:label>Subject</xf:label>
                        </xf:input>

                        <!-- Add new bf:subject elements that have a default value -->
                        <xf:trigger ref=".">
                            <xf:label>+</xf:label>
                            <xf:action ev:event="DOMActivate">
                                <xf:insert
                                    nodeset="../bf:subject[@rdf:resource[. = 'http://id.loc.gov/vocabulary/geographicAreas/s-ag']]"
                                    origin="instance('bf-Work-template')/bf:subject[@rdf:resource[. = 'http://id.loc.gov/vocabulary/geographicAreas/s-ag']]"
                                    at="last()" position="after"></xf:insert>
                            </xf:action>
                        </xf:trigger>

                        <!-- Delete bf:subject elements that have a default value -->
                        <xf:trigger
                            ref=".[count(../bf:subject[@rdf:resource[. = 'http://id.loc.gov/vocabulary/geographicAreas/s-ag']]) &gt; 1]">
                            <xf:label>-</xf:label>
                            <xf:delete ev:event="DOMActivate" nodeset="." at="last()"
                                if="count(../bf:subject[@rdf:resource[. = 'http://id.loc.gov/vocabulary/geographicAreas/s-ag']]) &gt; 1"
                            ></xf:delete>
                        </xf:trigger>
                    </div>
                </xf:repeat>

                <!-- Add new bf:subject elements that can take an arbitrary value -->
                <xf:trigger ref="bf:subject[@rdf:resource = '']">
                    <xf:label>+</xf:label>
                    <xf:action ev:event="DOMActivate">
                        <xf:insert nodeset="."
                            origin="instance('bf-Work-template')/bf:Work/bf:subject[@rdf:resource = '']"
                            at="last()" position="after"></xf:insert>
                    </xf:action>
                </xf:trigger>

                <!-- Delete bf:subject elements that can take an arbitrary value -->
                <xf:trigger
                    ref="bf:subject[@rdf:resource = ''][count(../bf:subject[@rdf:resource = '']) &gt; 1]">
                    <xf:label>-</xf:label>
                    <xf:action ev:event="DOMActivate">
                        <xf:delete nodeset="../bf:subject[@rdf:resource = '']" at="last()"
                            if="count(../bf:subject[@rdf:resource = '']) &gt; 1"></xf:delete>
                    </xf:action>
                </xf:trigger>

                <!-- Repeat bf:subject elements that can take an arbitrary value -->
                <xf:repeat nodeset="bf:subject[@rdf:resource = '']">
                    <div style="border:solid black 1px;">
                    <!-- Value of @rdf:resource is updated using jsPlumb library -->
                        <span class="label">Subject</span>
                        <br />
                        <span>Link to:</span>
                        <br />
                        <span class="connect-to">Work</span>
                        <br />
                        <span class="connect-to">Topic</span>
                        <br />
                        <span class="connect-to">Place</span>
                    </div>
                </xf:repeat>

            </xf:repeat>
        </div>
    </body>
</html>

【问题讨论】:

我不会对这个主题进行过多的阐述,但我会指出,基于 XML 的 RDF 操作很容易出错。可以使用 RDF/XML 以多种不同的方式编写同一个 RDF 图,并且给定的基于 XML 的方法并不适用于所有这些方法。如果可以,最好使用 RDF 处理工具将 RDF 处理为 RDF。请参阅my answer 到“如何在 Java 中使用 XPath 访问 OWL 文档?”有关可能出错的一些示例。 @JoshuaTaylor,谢谢。我意识到使用 RDF/XML 并不理想,但这里的范围相当有限:使用 XForms 进行数据输入并将生成的 RDF/XML 通过管道传输到三元存储。从那里,它可以用 SPARQL 和 RDF 处理工具来处理。据我所知,目前用于创建新 RDF 数据的数据输入工具并不多。我知道RDForms、Graphity 和Callimachus,但对于我们的特定项目,我们需要一些更灵活和可定制的东西。 你能发布一个更明确的测试用例吗?我将您的问题理解为 XPath 问题,对吗? XForms 2.0 允许使用变量,它可以简化您的 XPath 表达式。 XSLTForms 最新版本允许使用 var。 @tat 如果您对 XML 的生成方式有一定的控制权,那么使用 XML 并不是那么糟糕,而且在这种情况下,您可以这样做。即,您不必接受任意 RDF/XML。这是(尽管它可能很脆弱)将其视为 XML 的时候之一。 @AlainCouthures 我试图让这个例子更明确,但我不确定我是否成功。基本用例是我需要分别控制我的xf:model 中的两个bf:subject 元素。所以,我需要能够相互独立地插入和删除它们。我可以使用 XPath 谓词来做到这一点,但是一旦提供了值,@rdf:resource = '' 的谓词当然就不起作用了。我认为所需的功能类似于在 XSLT 中使用模式。 【参考方案1】:

目前,默认的 URI 都来自同一个命名空间 (http://id.loc.gov/vocabulary/...),所以临时的解决方案是过滤该值:not(starts-with(@rdf:resource, 'http://id.loc.gov/vocabulary/geographicAreas/'))。从长远来看,我正在研究nomisma.org,它体现了一种在 XForms 中进行链接数据词汇管理的更可持续的方法。

<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet href="http://localhost:8080/exist/apps/xsltforms/xsltforms.xsl" type="text/xsl"?>
<?xsltforms-options debug="yes"?>
<?css-conversion no?>
<?xml-model href="http://www.oxygenxml.com/1999/xhtml/xhtml-xforms.nvdl" schematypens="http://purl.oclc.org/dsdl/nvdl/ns/structure/1.0"?>
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:bf="http://bibframe.org/vocab/"
    xmlns:ev="http://www.w3.org/2001/xml-events"
    xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
    xmlns:rdfs="http://www.w3.org/2000/01/rdf-schema#" xmlns:xf="http://www.w3.org/2002/xforms"
    xmlns:xs="http://www.w3.org/2001/XMLSchema">
    <head>
        <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
        <title>Editor</title>
        <!--Model-->
        <xf:model id="rdf-model">
            <xf:instance id="graph">
                <rdf:RDF>
                    <bf:Work rdf:about="">
                        <bf:subject rdf:resource="http://id.loc.gov/vocabulary/geographicAreas/s-ag"></bf:subject>
                        <bf:subject rdf:resource="http://id.loc.gov/vocabulary/geographicAreas/s-bl"></bf:subject>
                        <bf:subject rdf:resource=""/>
                    </bf:Work>
                </rdf:RDF>
            </xf:instance>
            <!-- Template -->
            <xf:instance id="bf-Work-template">
                <rdf:RDF>
                    <bf:Work rdf:about="">
                        <bf:subject rdf:resource="http://id.loc.gov/vocabulary/geographicAreas/s-ag"></bf:subject>
                        <bf:subject rdf:resource="http://id.loc.gov/vocabulary/geographicAreas/s-bl"></bf:subject>
                        <bf:subject rdf:resource=""/>
                    </bf:Work>
                </rdf:RDF>
            </xf:instance>
        </xf:model>
    </head>
    <body>
        <div id="header">
            <h1>Editor</h1>
        </div>
        <div id="forms">
            <!-- Repeat for Work entity -->
            <xf:repeat nodeset="instance('graph')/bf:Work" id="repeat-Work-graph">

                <!-- Repeat bf:subject elements that have a default value. -->
                <xf:repeat
                    nodeset="bf:subject[@rdf:resource[. = 'http://id.loc.gov/vocabulary/geographicAreas/s-ag']]">
                    <div style="border:solid black 1px;">
                        <xf:input
                            ref="@rdf:resource[. = 'http://id.loc.gov/vocabulary/geographicAreas/s-ag']">
                            <xf:label>Subject</xf:label>
                        </xf:input>

                        <!-- Add new bf:subject elements that have a default value -->
                        <xf:trigger ref=".">
                            <xf:label>+</xf:label>
                            <xf:action ev:event="DOMActivate">
                                <xf:insert
                                    nodeset="../bf:subject[@rdf:resource[. = 'http://id.loc.gov/vocabulary/geographicAreas/s-ag']]"
                                    origin="instance('bf-Work-template')/bf:subject[@rdf:resource[. = 'http://id.loc.gov/vocabulary/geographicAreas/s-ag']]"
                                    at="last()" position="after"></xf:insert>
                            </xf:action>
                        </xf:trigger>

                        <!-- Delete bf:subject elements that have a default value -->
                        <xf:trigger
                            ref=".[count(../bf:subject[@rdf:resource[. = 'http://id.loc.gov/vocabulary/geographicAreas/s-ag']]) &gt; 1]">
                            <xf:label>-</xf:label>
                            <xf:delete ev:event="DOMActivate" nodeset="." at="last()"
                                if="count(../bf:subject[@rdf:resource[. = 'http://id.loc.gov/vocabulary/geographicAreas/s-ag']]) &gt; 1"
                                ></xf:delete>
                        </xf:trigger>
                    </div>
                </xf:repeat>

                <!-- Add new bf:subject elements that can take an arbitrary value -->
                <xf:trigger ref="bf:subject[not(starts-with(@rdf:resource, 'http://id.loc.gov/vocabulary/geographicAreas/'))]">
                    <xf:label>+</xf:label>
                    <xf:action ev:event="DOMActivate">
                        <xf:insert nodeset="."
                            origin="instance('bf-Work-template')/bf:Work/bf:subject[not(starts-with(@rdf:resource, 'http://id.loc.gov/vocabulary/geographicAreas/'))]"
                            at="last()" position="after"></xf:insert>
                    </xf:action>
                </xf:trigger>

                <!-- Delete bf:subject elements that can take an arbitrary value -->
                <xf:trigger
                    ref="bf:subject[not(starts-with(@rdf:resource, 'http://id.loc.gov/vocabulary/geographicAreas/'))][count(../bf:subject[not(starts-with(@rdf:resource, 'http://id.loc.gov/vocabulary/geographicAreas/'))]) &gt; 1]">
                    <xf:label>-</xf:label>
                    <xf:action ev:event="DOMActivate">
                        <xf:delete nodeset="../bf:subject[not(starts-with(@rdf:resource, 'http://id.loc.gov/vocabulary/geographicAreas/'))]" at="last()"
                            if="count(../bf:subject[not(starts-with(@rdf:resource, 'http://id.loc.gov/vocabulary/geographicAreas/'))]) &gt; 1"></xf:delete>
                    </xf:action>
                </xf:trigger>

                <!-- Repeat bf:subject elements that can take an arbitrary value -->
                <xf:repeat nodeset="bf:subject[not(starts-with(@rdf:resource, 'http://id.loc.gov/vocabulary/geographicAreas/'))]">
                    <div style="border:solid black 1px;">
                        <!-- Value of @rdf:resource is updated using jsPlumb library -->
                        <span class="label">Subject</span>
                        <br />
                        <span>Link to:</span>
                        <br />
                        <span class="connect-to">Work</span>
                        <br />
                        <span class="connect-to">Topic</span>
                        <br />
                        <span class="connect-to">Place</span>
                    </div>
                </xf:repeat>

            </xf:repeat>
        </div>
    </body>
</html>

【讨论】:

【参考方案2】:

如果您正在寻找一种 XPath 方法来了解元素是第一个还是第二个子元素,您可以考虑使用轴,例如 .[preceding-sibling::bf:subject]

【讨论】:

以上是关于XForms 重复:相同的元素名称,不同的值约束的主要内容,如果未能解决你的问题,请参考以下文章

Python基础(3) - 去掉列表或元组中的重复元素

在IB中或以编程方式重用不同场景的视图

使用 xforms 在元素中换行

具有相同元素名称但属性值不同的XML的XSD架构[关闭]

组合两个函数时显示错误(元素重复)

类变量对于不同的实例具有不同的值[重复]