XSLT 将元素值分配给变量:与 Altova XML Spy 配合使用,但在 .NET 应用程序中失败
我想紧紧抓住微软员工的后脑勺头发,用它作为杠杆,用力反复用力敲打他的头在坚硬的表面上!这会让我感觉就像现在解决这个问题一样好。
我有一个简单的 XML 消息,如下所示:
<?xml version="1.0" encoding="utf-8"?>
<message>
<cmd id="instrument_status">
<status_id>1</status_id>
</cmd>
</message>
我正在使用的设备上的 Web 服务返回多个此类消息,我将它们转换为不同的格式。对于上述消息,新格式如下所示:
<?xml version="1.0" encoding="UTF-8"?>
<grf:message xmlns:grf="http://www.company.com/schemas/device/version001">
<grf:messageHeader>
<grf:messageType>instrumentStatus</grf:messageType>
</grf:messageHeader>
<grf:messageBody>
<grf:instrumentStatusBody>
<grf:statusId>Running</grf:statusId>
</grf:instrumentStatusBody>
</grf:messageBody>
</grf:message>
XML 中的 status_id 整数值有一个映射,如下所示:
status-id Meaning
========= =======
0 Ready
1 Running
2 NotReady
3 PoweringUp
4 PoweringDown
5 PoweredUp
6 PoweredDown
7 Tuning
8 Error
当我使用 Altova XMLSpy 时,我的 XSLT 工作正常并给出正确的输出,但当我运行 . NET 应用程序中,当 status_id 整数的映射转换为允许的枚举字符串之一时,我遇到了失败。 MS XSLT 处理器没有返回枚举值,而是返回一个空字符串,并且我得到一个空的
以下是我的 XSLT 代码,删除了一些部分以减少空间量:
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:fo="http://www.w3.org/1999/XSL/Format"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
xmlns:fn="http://www.w3.org/2005/xpath-functions"
xmlns:msxsl="urn:schemas-microsoft-com:xslt"
xmlns:grf="http://www.company.com/schemas/device/version001"
exclude-result-prefixes="#default">
<xsl:template match="/">
<xsl:apply-templates select="message"/>
</xsl:template>
<xsl:template match="message">
<xsl:element name="grf:message">
<xsl:apply-templates select="/message/cmd/@id"/>
</xsl:element>
</xsl:template>
<xsl:template match="/message/cmd/@id">
<xsl:variable name="_commandType" select="/message/cmd/@id"/>
<!-- Following line works in Altova XMLSpy, but fails in .NET app. ??? -->
<xsl:variable name="_statusIdValue" select="/message/cmd/status_id"/>
<xsl:element name="grf:messageHeader">
<xsl:element name="grf:messageType">
<xsl:choose>
<xsl:when test="$_commandType = 'api_info'">
<xsl:text>apiInfo</xsl:text>
</xsl:when>
<xsl:when test="$_commandType = 'instrument_status'">
<xsl:text>instrumentStatus</xsl:text>
</xsl:when>
</xsl:choose>
</xsl:element>
</xsl:element>
<xsl:element name="grf:messageBody">
<xsl:choose>
<xsl:when test="$_commandType = 'api_info'">
<xsl:element name="grf:apiInfoBody">
<xsl:element name="grf:apiVersion">
<xsl:value-of select="/message/cmd/api-version"/>
</xsl:element>
<xsl:element name="grf:apiBuild">
<xsl:value-of select="/message/cmd/api-build"/>
</xsl:element>
</xsl:element>
</xsl:when>
<xsl:when test="$_commandType = 'instrument_status'">
<xsl:element name="grf:instrumentStatusBody">
<xsl:element name="grf:statusId">
<xsl:choose>
<xsl:when test="$_statusIdValue = '0'">
<xsl:text>Ready</xsl:text>
</xsl:when>
<xsl:when test="$_statusIdValue = '1'">
<xsl:text>Running</xsl:text>
</xsl:when>
<xsl:when test="$_statusIdValue = '2'">
<xsl:text>NotReady</xsl:text>
</xsl:when>
<xsl:when test="$_statusIdValue = '3'">
<xsl:text>PoweringUp</xsl:text>
</xsl:when>
<xsl:when test="$_statusIdValue = '4'">
<xsl:text>PoweringDown</xsl:text>
</xsl:when>
<xsl:when test="$_statusIdValue = '5'">
<xsl:text>PoweredUp</xsl:text>
</xsl:when>
<xsl:when test="$_statusIdValue = '6'">
<xsl:text>PoweredDown</xsl:text>
</xsl:when>
<xsl:when test="$_statusIdValue = '7'">
<xsl:text>Tuning</xsl:text>
</xsl:when>
<xsl:when test="$_statusIdValue = '8'">
<xsl:text>Error</xsl:text>
</xsl:when>
</xsl:choose>
</xsl:element>
</xsl:element>
</xsl:when>
</xsl:choose>
</xsl:element>
</xsl:template>
</xsl:stylesheet>
是否有 XSLT 1.0 代码在 Altova XMLSpy 和 MS XSLT 处理器中表现相同?
谢谢,
AlarmTripper
I want to tightly grasp the hair on the back of a Microsoft employee's head, using it as leverage to pound his head forcefully and repeatedly against a hard surface! That would make me feel nearly as good as solving this problem right now.
I've got a simple XML message that looks like this:
<?xml version="1.0" encoding="utf-8"?>
<message>
<cmd id="instrument_status">
<status_id>1</status_id>
</cmd>
</message>
A web service on the device I'm working with returns several such messages and I'm converting them to a different format. For the above message the new format would look like the following:
<?xml version="1.0" encoding="UTF-8"?>
<grf:message xmlns:grf="http://www.company.com/schemas/device/version001">
<grf:messageHeader>
<grf:messageType>instrumentStatus</grf:messageType>
</grf:messageHeader>
<grf:messageBody>
<grf:instrumentStatusBody>
<grf:statusId>Running</grf:statusId>
</grf:instrumentStatusBody>
</grf:messageBody>
</grf:message>
There is a mapping for status_id integer values in the XML as follows:
status-id Meaning
========= =======
0 Ready
1 Running
2 NotReady
3 PoweringUp
4 PoweringDown
5 PoweredUp
6 PoweredDown
7 Tuning
8 Error
My XSLT is working correctly and giving me the correct output when I use Altova XMLSpy, but when I run my .NET application, I'm getting a failure at the point where the mapping for the status_id integer is converted to one of the allowable enumerated strings. Instead of getting the enumerated value, the MS XSLT processor returns an empty string and I get an empty <status_id/> element in the output XML.
The following is my XSLT code with some sections removed to reduce the amount of space:
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:fo="http://www.w3.org/1999/XSL/Format"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
xmlns:fn="http://www.w3.org/2005/xpath-functions"
xmlns:msxsl="urn:schemas-microsoft-com:xslt"
xmlns:grf="http://www.company.com/schemas/device/version001"
exclude-result-prefixes="#default">
<xsl:template match="/">
<xsl:apply-templates select="message"/>
</xsl:template>
<xsl:template match="message">
<xsl:element name="grf:message">
<xsl:apply-templates select="/message/cmd/@id"/>
</xsl:element>
</xsl:template>
<xsl:template match="/message/cmd/@id">
<xsl:variable name="_commandType" select="/message/cmd/@id"/>
<!-- Following line works in Altova XMLSpy, but fails in .NET app. ??? -->
<xsl:variable name="_statusIdValue" select="/message/cmd/status_id"/>
<xsl:element name="grf:messageHeader">
<xsl:element name="grf:messageType">
<xsl:choose>
<xsl:when test="$_commandType = 'api_info'">
<xsl:text>apiInfo</xsl:text>
</xsl:when>
<xsl:when test="$_commandType = 'instrument_status'">
<xsl:text>instrumentStatus</xsl:text>
</xsl:when>
</xsl:choose>
</xsl:element>
</xsl:element>
<xsl:element name="grf:messageBody">
<xsl:choose>
<xsl:when test="$_commandType = 'api_info'">
<xsl:element name="grf:apiInfoBody">
<xsl:element name="grf:apiVersion">
<xsl:value-of select="/message/cmd/api-version"/>
</xsl:element>
<xsl:element name="grf:apiBuild">
<xsl:value-of select="/message/cmd/api-build"/>
</xsl:element>
</xsl:element>
</xsl:when>
<xsl:when test="$_commandType = 'instrument_status'">
<xsl:element name="grf:instrumentStatusBody">
<xsl:element name="grf:statusId">
<xsl:choose>
<xsl:when test="$_statusIdValue = '0'">
<xsl:text>Ready</xsl:text>
</xsl:when>
<xsl:when test="$_statusIdValue = '1'">
<xsl:text>Running</xsl:text>
</xsl:when>
<xsl:when test="$_statusIdValue = '2'">
<xsl:text>NotReady</xsl:text>
</xsl:when>
<xsl:when test="$_statusIdValue = '3'">
<xsl:text>PoweringUp</xsl:text>
</xsl:when>
<xsl:when test="$_statusIdValue = '4'">
<xsl:text>PoweringDown</xsl:text>
</xsl:when>
<xsl:when test="$_statusIdValue = '5'">
<xsl:text>PoweredUp</xsl:text>
</xsl:when>
<xsl:when test="$_statusIdValue = '6'">
<xsl:text>PoweredDown</xsl:text>
</xsl:when>
<xsl:when test="$_statusIdValue = '7'">
<xsl:text>Tuning</xsl:text>
</xsl:when>
<xsl:when test="$_statusIdValue = '8'">
<xsl:text>Error</xsl:text>
</xsl:when>
</xsl:choose>
</xsl:element>
</xsl:element>
</xsl:when>
</xsl:choose>
</xsl:element>
</xsl:template>
</xsl:stylesheet>
Is there XSLT 1.0 code that will behave the same in both Altova XMLSpy and the MS XSLT processor?
Thanks,
AlarmTripper
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(5)
需要注意的一件事是,在匹配“message”元素的模板中,您执行此操作,
这实际上会尝试匹配 XML 中相对于文档根的第一条消息,无论您当前所在的消息是什么。它不是相对于当前节点进行选择。就您而言,看起来只会有一条消息,因此这里不会出现问题,但在其他情况下会出现问题。
匹配元素而不是属性也可能更常见,尤其是在您想要处理元素的子元素的情况下。因此,您可能会用此替换上面的行然后
,对于与其匹配的模板,而不是当前执行此操作
您将执行此操作
接下来,在此模板中,您可以尝试用更简单的 select 语句替换变量
看看是否可以有所作为。
One thing to note is that in the template that matches the "message" element, you do this
This will actually try to match the very first message in the XML relative to the document root, regardless of what message you are currently on. It is not selecting relative to the current node. In your case, it looks like there will only ever be one message, so it won't be an issue here, but it would be in other cases.
It is also probably more common to match on elements, rather than attributes, especially where you want to process child elements of an element. So, you would probably replace the above line with this instead
Then, for the template that matches it, instead of doing this currently
You would do this instead
Next, within this template, you could try replacing your variables with simpler select statements
See if that makes a difference.
您的转型方式过于复杂化。尝试这个相当简单的样式表:
我删除了所有看似多余的命名空间定义(根据需要将它们添加回来)并将样式表放入默认命名空间中。这意味着您不再需要在每个元素上添加
'grf:'
前缀,而无需更改实际结果:请注意我如何使用不同的匹配表达式和不同的模板模式在右侧输出适当的元素情况。这样,任何
或
都变得不必要,从而形成更干净、更易于维护的样式表。另外,通常不需要显式定义
,除非您想输出具有动态名称的元素。在所有其他情况下,您可以直接编写该元素。很抱歉,我无法确定为什么您的样式表没有按预期运行。它对我有用,而且看起来还不错。
如果以上任何内容不清楚,请随时询问。
You are way over-complicating your transformation. Try this considerably simpler stylesheet:
I got rid of all your seemingly superfluous namespace definitions (add them back as you need them) and put your stylesheet into a default namespace. This means you don't need a
'grf:'
prefix on every element anymore, without changing the actual result:Note how I use different match expressions and different template modes to output the appropriate elements in the right situations. This way any
<xsl:variable>
or<xsl:choose>
become unnecessary, making for a cleaner and more maintainable stylesheet.Also, usually there is no need to define
<xsl:element>
explicitly, unless you want to output elements with dynamic names. In all other cases, you can write the element straight-away.I'm sorry that I can't say for sure why your stylesheet does not run as intended. It works for me, and it looks okay(ish).
Do not hesitate to ask if any of the above is unclear.
自从我编写任何 xslt 代码以来已经有很长一段时间了,但是根据我所看到的,您也许能够更改这一行:
to
这应该告诉它选择元素的内容而不是节点本身。
有点像当您执行 value-of 操作并且想要节点的文本内容时。你也会做同样的事情。
例如,如果您想吐出状态 ID 号,您可以使用以下命令:
It's been a long time since I've coded up any xslt's but based on what I'm seeing you might be able to change this line:
to
That should tell it to select the content of the element vs the node itself.
Kind of like when you do a value-of operation and you want the node's text content. you would do the same thing.
For example if you wanted to spit back out the status id number you could use the following:
好的,我发现如果我使用以下行来分配 _statusIdValue 变量,则代码将与 XslCompiledTransform 类一起正确运行:
这将替换原来的行:
但是,适用于 XslCompiledTransform 类的分配不会'不能与 Altova XMLSpy 一起使用。是否存在可以在 Altova XMLSpy 编辑器和 XslCompiledTransform 类中正常工作的分配变体?
谢谢,
AlarmTripper
OK, I found out that if I use the following line to assign the _statusIdValue variable, then the code will function correctly with the XslCompiledTransform class:
This replaces the original line which was:
However, the assignment that works in for the XslCompiledTransform class doesn't work with Altova XMLSpy. Is there a variant of the assignment that will work correctly in both the Altova XMLSpy editor and with the XslCompiledTransform class?
Thanks,
AlarmTripper
这是如此糟糕的编码,我实际上很高兴它在 .NET 中不起作用,我建议您重写样式表。
试试这个:
我只使用一个
,如果您觉得合适,可以重构。This is such terrible coding I'm actually glad it doesn't work in .NET, I suggest you rewrite your stylesheet.
Try this:
I'm using only one
<xsl:template>
, you can refactor if you feel it's appropiate.