使用 XSLT 根据架构创建有效的 XML
我正在根据架构中定义的类型对元素列表进行排序。我知道 XSLT 可以针对给定模式进行验证,但我想要做的是在执行操作之前进行检查以确保我的操作(在本例中为副本)有效。
简化的代码:
传入数据:
<?xml version="1.0"?>
<sch:foo xmlns:sch="http://www.whatever.com/schema">
<sch:attr1>val1</sch:attr1>
<sch:attr2>val2</sch:attr2>
<sch:attr3>val3</sch:attr3>
<sch:attr4>val4</sch:attr4>
</sch:foo>
所需的传出数据:
<?xml version="1.0"?>
<sch:fooOut xmlns:sch="http://www.whatever.com/schema">
<sch:bar>
<sch:attr1>val1</sch:attr1>
<sch:attr2>val2</sch:attr2>
</sch:bar>
<sch:stuff>
<sch:attr3>val3</sch:attr3>
<sch:attr4>val4</sch:attr4>
</sch:stuff>
</sch:fooOut>
架构文件中的某处:
<complexType name="fooOut">
<sequence>
<!-- ... -->
<element name="bar">
<complexType>
<sequence>
<element name="attr1" type="sch:myBarType" minOccurs="0" maxOccurs="unbounded"/>
<element name="attr2" type="sch:myBarType" minOccurs="0" maxOccurs="unbounded"/>
</sequence>
</complexType>
</element>
<element name="stuff">
<complexType>
<sequence>
<element name="attr3" type="sch:myStuffType" minOccurs="0" maxOccurs="unbounded"/>
<element name="attr4" type="sch:myStuffType" minOccurs="0" maxOccurs="unbounded"/>
</sequence>
</complexType>
</element>
</sequence>
</complexType>
(我刚刚学习如何使用 .xsd,换句话说:只有 attr1
和 attr2
可以进入 bar
,并且只有 attr3
和 >attr4
可以放在 stuff
中)
基本上,在实际情况中,标签太多,无法手动将它们分开。我想知道是否有一种方法可以检查架构,以确定元素是否适合它们需要排序的类型。如果他们属于一个类别,他们应该只属于该类别。
感谢所有帮助!
编辑
@Alejandro 的代码适用于上述基本伪代码,但我在我的文件中实现它时遇到了麻烦,这些文件更复杂。因此,我添加了一个更复杂的示例:
传入数据
<?xml version="1.0"?>
<sch:foo xmlns:sch="http://www.whatever.com/schema">
<sch:nesting>
<sch:myGroup>
<sch:mustHaveData>asdf</sch:mustHaveData>
<sch:attr1>val1</sch:attr1>
<sch:attr2>val2</sch:attr2>
<sch:attr3>val3</sch:attr3>
<sch:attr4>val4</sch:attr4>
</sch:myGroup>
<sch:myGroup>
<sch:mustHaveData>asdf2</sch:mustHaveData>
<sch:attr1>val5</sch:attr1>
<sch:attr2>val6</sch:attr2>
<sch:attr3>val7</sch:attr3>
<sch:attr4>val8</sch:attr4>
</sch:myGroup>
</sch:nesting>
</sch:foo>
所需的传出数据:
<?xml version="1.0"?>
<sch:fooOut xmlns:sch="http://www.whatever.com/schema">
<sch:anotherGroup>
<sch:name>foobar</sch:name>
<sch:bar>
<sch:attr1>val1</sch:attr1>
<sch:attr2>val2</sch:attr2>
</sch:bar>
<sch:stuff>
<sch:attr3>val3</sch:attr3>
<sch:attr4>val4</sch:attr4>
</sch:stuff>
</sch:anotherGroup>
<sch:anotherGroup>
<sch:name>foobar</sch:name>
<sch:bar>
<sch:attr1>val5</sch:attr1>
<sch:attr2>val6</sch:attr2>
</sch:bar>
<sch:stuff>
<sch:attr3>val7</sch:attr3>
<sch:attr4>val8</sch:attr4>
</sch:stuff>
</sch:anotherGroup>
</sch:fooOut>
架构文件中的某处:(比上次更准确)
<complexType name="anotherGroup">
<sequence>
<element name="name" type="xs:string" minOccurs="1" maxOccurs="1" />
<element name="bar" type="barListType" minOccurs="0" maxOccurs="1" />
<element name="stuff" type="stuffListType" minOccurs="0" maxOccurs="1" />
</sequence>
</complexType>
<!-- in another .xsd -->
<complexType name="barListType">
<group ref="barGroup" maxOccurs="unbounded" />
</complexType>
<complexType name="stuffListType">
<group ref="stuffGroup" maxOccurs="unbounded" />
</complexType>
<!-- in yet another .xsd -->
<group name="barGroup">
<choice>
<element name="attr1" type="blah1" minOccurs="0" maxOccurs="1" />
<element name="attr2" type="blah2" minOccurs="0" maxOccurs="1" />
<!-- etc -->
</choice>
</group>
<group name ="stuffGroup">
<choice>
<element name="attr3" type="blah3" minOccurs="0" maxOccurs="1" />
<element name="attr4" type="blah4" minOccurs="0" maxOccurs="1" />
<!-- etc -->
</choice>
</group>
最后是我的 xsl 文件
<xsl:output method="xml" encoding="UTF-8" />
<xsl:param name="schema-name" select="'myXsd.xsd'" />
<xsl:template match="/">
<xsl:apply-templates select="document($schema-name)/xs:complexType[@*]" />
<xsl:apply-templates select="sch:nesting"/>
</xsl:template>
<xsl:template match="sch:nesting/xs:element[xs:complexType]">
<xsl:element name="{@name}" namespace="http://www.whatever.com/schema">
<xsl:apply-templates />
</xsl:element>
</xsl:template>
<xsl:template match="sch:nesting/xs:element[not(xs:complexType)]">
<xsl:element name="{@name}" namespace="http://www.whatever.com/schema">
<xsl:value-of select="/*/sch:*[name()=current()/@name or
substring-after(name(),':')=current()/@name]"/>
</xsl:element>
</xsl:template>
<xsl:template match="sch:nesting">
<xsl:element name="anotherGroup">
<xsl:element name="name">
<!-- Whatever -->
</xsl:element>
<xsl:apply-templates /> <!-- I want to drop the data here, but this is definitely wrong -->
</xsl:element>
</xsl:template>
</xsl:stylesheet>
再次感谢您的帮助!
编辑 2
因此,我忘记对我的数据文件进行一项小更改。除了布局之外,其他所有内容都应该相同,布局如下所示:
<?xml version="1.0"?>
<sch:foo xmlns:sch="http://www.whatever.com/schema">
<sch:nesting>
<sch:myGroup>
<inner1>
<sch:mustHaveData>asdf</sch:mustHaveData>
<sch:attr1>val1</sch:attr1>
</inner1>
<inner2>
<sch:attr2>val2</sch:attr2>
<sch:attr3>val3</sch:attr3>
<sch:attr4>val4</sch:attr4>
</inner2>
</sch:myGroup>
<sch:myGroup>
<inner1>
<sch:mustHaveData>asdf2</sch:mustHaveData>
<sch:attr1>val5</sch:attr1>
</inner1>
<inner2>
<sch:attr2>val6</sch:attr2>
<sch:attr3>val7</sch:attr3>
<sch:attr4>val8</sch:attr4>
</inner2>
</sch:myGroup>
</sch:nesting>
</sch:foo>
我在这里试图说明的是,组内有子类别,我需要匹配这些子类别内的所有内容。
亚历杭德罗,为了让这值得你花时间(因为你提供了如此惊人的帮助),你应该将你的回应放在一个新的答案中,当我尝试它并使其发挥作用时,我会投票并接受该答案。
再次感谢!你真是一个救星!
编辑 3
我找到了想要的结果。 行。
<xsl:copy-of select="*[local-name()=document($schema-name)/*/*
我更改了说“
<xsl:copy-of select="*/*[local-name()=document($schema-name)/*/*
哪个给了我我需要的额外位”的
I'm sorting a list of elements based on type, as defined in my schema. I know that XSLT can validate against a given schema, but what I want to do is check to make sure that my operation (a copy in this case) is valid before I do it.
Simplified code:
Incoming Data:
<?xml version="1.0"?>
<sch:foo xmlns:sch="http://www.whatever.com/schema">
<sch:attr1>val1</sch:attr1>
<sch:attr2>val2</sch:attr2>
<sch:attr3>val3</sch:attr3>
<sch:attr4>val4</sch:attr4>
</sch:foo>
Desired Outgoing Data:
<?xml version="1.0"?>
<sch:fooOut xmlns:sch="http://www.whatever.com/schema">
<sch:bar>
<sch:attr1>val1</sch:attr1>
<sch:attr2>val2</sch:attr2>
</sch:bar>
<sch:stuff>
<sch:attr3>val3</sch:attr3>
<sch:attr4>val4</sch:attr4>
</sch:stuff>
</sch:fooOut>
Somewhere in the Schema File:
<complexType name="fooOut">
<sequence>
<!-- ... -->
<element name="bar">
<complexType>
<sequence>
<element name="attr1" type="sch:myBarType" minOccurs="0" maxOccurs="unbounded"/>
<element name="attr2" type="sch:myBarType" minOccurs="0" maxOccurs="unbounded"/>
</sequence>
</complexType>
</element>
<element name="stuff">
<complexType>
<sequence>
<element name="attr3" type="sch:myStuffType" minOccurs="0" maxOccurs="unbounded"/>
<element name="attr4" type="sch:myStuffType" minOccurs="0" maxOccurs="unbounded"/>
</sequence>
</complexType>
</element>
</sequence>
</complexType>
(I'm just learning how to work with .xsd's, so to say it in words: only attr1
and attr2
can go in bar
, and only attr3
and attr4
can go in stuff
)
Basically, in the real situation there's too many tags to feasibly separate them manually. I'd like to know if there's a way to check the schema for whether the elements are right for whichever type they need to be sorted into. If they belong in one category, they should go to just that category.
All help is appreciated and thanks!
EDIT
@Alejandro's code works for the above basic pseudocode, but I'm having trouble implementing it in my files, which are more complex. For that reason, I'm adding a more complicated example:
Incoming Data
<?xml version="1.0"?>
<sch:foo xmlns:sch="http://www.whatever.com/schema">
<sch:nesting>
<sch:myGroup>
<sch:mustHaveData>asdf</sch:mustHaveData>
<sch:attr1>val1</sch:attr1>
<sch:attr2>val2</sch:attr2>
<sch:attr3>val3</sch:attr3>
<sch:attr4>val4</sch:attr4>
</sch:myGroup>
<sch:myGroup>
<sch:mustHaveData>asdf2</sch:mustHaveData>
<sch:attr1>val5</sch:attr1>
<sch:attr2>val6</sch:attr2>
<sch:attr3>val7</sch:attr3>
<sch:attr4>val8</sch:attr4>
</sch:myGroup>
</sch:nesting>
</sch:foo>
Desired Outgoing Data:
<?xml version="1.0"?>
<sch:fooOut xmlns:sch="http://www.whatever.com/schema">
<sch:anotherGroup>
<sch:name>foobar</sch:name>
<sch:bar>
<sch:attr1>val1</sch:attr1>
<sch:attr2>val2</sch:attr2>
</sch:bar>
<sch:stuff>
<sch:attr3>val3</sch:attr3>
<sch:attr4>val4</sch:attr4>
</sch:stuff>
</sch:anotherGroup>
<sch:anotherGroup>
<sch:name>foobar</sch:name>
<sch:bar>
<sch:attr1>val5</sch:attr1>
<sch:attr2>val6</sch:attr2>
</sch:bar>
<sch:stuff>
<sch:attr3>val7</sch:attr3>
<sch:attr4>val8</sch:attr4>
</sch:stuff>
</sch:anotherGroup>
</sch:fooOut>
Somewhere in the Schema Files: (and somewhat more accurate than last time)
<complexType name="anotherGroup">
<sequence>
<element name="name" type="xs:string" minOccurs="1" maxOccurs="1" />
<element name="bar" type="barListType" minOccurs="0" maxOccurs="1" />
<element name="stuff" type="stuffListType" minOccurs="0" maxOccurs="1" />
</sequence>
</complexType>
<!-- in another .xsd -->
<complexType name="barListType">
<group ref="barGroup" maxOccurs="unbounded" />
</complexType>
<complexType name="stuffListType">
<group ref="stuffGroup" maxOccurs="unbounded" />
</complexType>
<!-- in yet another .xsd -->
<group name="barGroup">
<choice>
<element name="attr1" type="blah1" minOccurs="0" maxOccurs="1" />
<element name="attr2" type="blah2" minOccurs="0" maxOccurs="1" />
<!-- etc -->
</choice>
</group>
<group name ="stuffGroup">
<choice>
<element name="attr3" type="blah3" minOccurs="0" maxOccurs="1" />
<element name="attr4" type="blah4" minOccurs="0" maxOccurs="1" />
<!-- etc -->
</choice>
</group>
Finally, my xsl file
<xsl:output method="xml" encoding="UTF-8" />
<xsl:param name="schema-name" select="'myXsd.xsd'" />
<xsl:template match="/">
<xsl:apply-templates select="document($schema-name)/xs:complexType[@*]" />
<xsl:apply-templates select="sch:nesting"/>
</xsl:template>
<xsl:template match="sch:nesting/xs:element[xs:complexType]">
<xsl:element name="{@name}" namespace="http://www.whatever.com/schema">
<xsl:apply-templates />
</xsl:element>
</xsl:template>
<xsl:template match="sch:nesting/xs:element[not(xs:complexType)]">
<xsl:element name="{@name}" namespace="http://www.whatever.com/schema">
<xsl:value-of select="/*/sch:*[name()=current()/@name or
substring-after(name(),':')=current()/@name]"/>
</xsl:element>
</xsl:template>
<xsl:template match="sch:nesting">
<xsl:element name="anotherGroup">
<xsl:element name="name">
<!-- Whatever -->
</xsl:element>
<xsl:apply-templates /> <!-- I want to drop the data here, but this is definitely wrong -->
</xsl:element>
</xsl:template>
</xsl:stylesheet>
Thanks again for the help!
EDIT 2
So there's one minor change that I forgot to put in about my data file. Everything else should be the same except for the layout, which is nested like so:
<?xml version="1.0"?>
<sch:foo xmlns:sch="http://www.whatever.com/schema">
<sch:nesting>
<sch:myGroup>
<inner1>
<sch:mustHaveData>asdf</sch:mustHaveData>
<sch:attr1>val1</sch:attr1>
</inner1>
<inner2>
<sch:attr2>val2</sch:attr2>
<sch:attr3>val3</sch:attr3>
<sch:attr4>val4</sch:attr4>
</inner2>
</sch:myGroup>
<sch:myGroup>
<inner1>
<sch:mustHaveData>asdf2</sch:mustHaveData>
<sch:attr1>val5</sch:attr1>
</inner1>
<inner2>
<sch:attr2>val6</sch:attr2>
<sch:attr3>val7</sch:attr3>
<sch:attr4>val8</sch:attr4>
</inner2>
</sch:myGroup>
</sch:nesting>
</sch:foo>
What I'm trying to illustrate here is that within the groups are subcategories, and I need to match everything within those subcategories.
Alejandro, to make this worth your while (since you have been such an amazing help), you should put your response in a new answer, and when I try it and get it to work, I will up-vote and accept that answer.
Thanks again! You're really a lifesaver!
EDIT 3
I figured out the desired result. I changed the lines that said
<xsl:copy-of select="*[local-name()=document($schema-name)/*/*
to
<xsl:copy-of select="*/*[local-name()=document($schema-name)/*/*
Which gave me the extra bit that I needed.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
架构(“schema.xs”):
输入:
样式表:
结果:
注意:这是一个 XSLT 1.0 解决方案,但我认为使用 XSLT 2.0 可以做得更好。- 另外,如果架构是一种更合适的方法(元素定义和类型定义)此方法可以与键一起使用。另一种方法(输入驱动而不是模式驱动)尽管更复杂,但也可以完成,但我没有时间了。
编辑:现在,假设这些架构
schemaA.xsd
schemaB.xsd
schemaC.xsd
这个样式表:
结果:
注意: 这有效,但你的第二个问题(或问题)表明有不是一般情况下的样式表。为什么?因为使用 XSLT,您必须将输入(具有众所周知的模式)绑定到输出(也具有众所周知的模式)。所以这个特定的样式表可以完成这项工作:
结果:
Schema ("schema.xs"):
Input:
Stylesheet:
Result:
Note: This is an XSLT 1.0 solution, but I think this can be done better with XSLT 2.0.- Also, if the schema is a more proper one (elements definitions and types definitions) this method could work with keys. Another method (input driven and not schema driven), despite being more complex, also could be done but I'm runnig out of time.
Edit: Now, suppose these schemas
schemaA.xsd
schemaB.xsd
schemaC.xsd
This stylesheet:
Result:
Note: This works but your second questions (or problem) shows that there is no general case stylesheet. Why? Because with XSLT you must bind an input (with well known schema) to an output (with well known schema too). So this specific stylesheet could do the job:
Result:
不确定您在这里寻找什么。无论您如何指示,XSLT 都会从源文档复制到目标文档。例如:
您正在寻找什么样的验证?如果您正在寻找更动态的解决方案,那么使用 XSLT 以外的其他解决方案可能会更好。
Not sure what you're looking for here. XSLT will copy from the source doc to the destination doc however you tell it to. For example:
What kind of validation are you looking for? You may be better off using something other than XSLT if you're looking for a more dynamic solution.