开发者

How to eliminate xmlns="" entries produced by XSLT transform of one XML doc to another XML doc

开发者 https://www.devze.com 2022-12-13 09:23 出处:网络
Ok, I\'ve seen numerous variations on this question, but none exactly answer what I\'m trying to solve and perhaps I\'m just too dense to see how to apply one of the other answers to what I\'m trying

Ok, I've seen numerous variations on this question, but none exactly answer what I'm trying to solve and perhaps I'm just too dense to see how to apply one of the other answers to what I'm trying to do.

I have some XML that looks something like the following:

<?xml version="1.0" encoding="utf-8"?>
<message>
  <cmd id="api_info">
    <api-version>1.0</api-version>
    <api-build>1.0.0.0</api-build>
  </cmd>
</message>

Now I have an XSLT transform that I'm applying to this XML. The XSLT is similar to the following:

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet 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"
                version="2.0">

    <xsl:output method="xml" version="1.0" indent="yes"/>

    <xsl:template match="/">
        <xsl:apply-templates select="message"/>
    </xsl:template>

    <xsl:template match="message">
        <xsl:element name="message" xmlns="http://www.companyname.com/schemas/product/Version001">
            <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"/>
        <xsl:element name="messageHeader">
            <xsl:element name="cmdType">
                <xsl:value-of select="$_commandType"/>
            </xsl:element>
        </xsl:element>

        <xsl:element name="messageBody">
            <xsl:choose>
                <xsl:when test="$_commandType = 'api_info'">
                    <xsl:element name="apiInfoBody">
                        <xsl:element name="apiVersion">
                            <xsl:value-of select="/message/cmd/api-version"/>
                        </xsl:element>
                        <xsl:element name="apiBuild">
                            <xsl:value-of select="/message/cmd/api-build"/>
                        </xsl:element>
                    </xsl:element>
                </xsl:when>
                <xsl:when test="$_commandType = 'communicationError'">
                    <xsl:element name="communicationErrorBody">
                        <xsl:element name="errorCode">
                            <xsl:value-of select="error-code"/>
                        </xsl:element>
                        <xsl:element name="badCmd">
                            <xsl:value-of select="bad-cmd"/>
                        </xsl:element>
                    </xsl:element>
                </xsl:when>
            </xsl:choose>
        </xsl:element>
    </xsl:template>
</xsl:stylesheet>

The output I get is basically what I want and looks like the following:

<?xml version="1.0" encoding="UTF-8"?>
<message xmlns="http://www.companyname.com/schemas/product/Version001">
    <messageHeader xmlns="">
        <cmdType>api_info</cmdType>
    </messageHeader>
    <messageBody xmlns="">
        <apiInfoBody>
            <apiVersion>1.0</apiVersion>
            <apiBuild>1.0.0.0</apiBuild>
        </apiInfoBody>
    </messageBody>
</message>

But what I don't want are the xmlns="" attributes in the <messageHeader> and <messageBody> elements.

Now I've found that if I explicitly specify the namespace in the XSLT for those elements, then the unwanted attribute gets pushed down one level to the children of those attributes.

I could just go through my entire XSLT and explicitly add the xmlns=""http://www.companyname.com/schemas/product/Version001" attribute to each of my xsl:element definitions, but I know that there must be a more elegant way. We programmers are far too lazy to not have a shortcut for this kind of nonsense. If my XSLT didn't consist of something as simple as the shortened example, I be tempted to do it that way. But I know 开发者_运维知识库there must be a better way.

Does anyone know what I'm missing here?

Thanks,

AlarmTripper


Use exclude-result-prefixes on the xsl:stylesheet tag with the prefix "#default"

The reference in w3c for this is HERE

EDIT: OK, I should have studied your XSL more carefully. Move the xmlns on the message tag up to the stylesheet tag. This will put ALL the result elements in the same namespace and result in one namespace attribute on the message tag. I ran this in Oxygen/XML and got the output you want.


Ok, I figured it out.

The XSLT I want looks like this:

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="2.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"
                exclude-result-prefixes="xsl fo xs fn">

    <xsl:output method="xml" version="1.0" indent="yes"/>

    <xsl:template match="/">
        <xsl:apply-templates select="message"/>
    </xsl:template>

    <xsl:template match="message">
        <message namespace="http://www.companyname.com/schemas/product/Version001">
            <xsl:apply-templates select="/message/cmd/@id"/>
        </message>
    </xsl:template>

    <xsl:template match="/message/cmd/@id">
        <xsl:variable name="_commandType" select="/message/cmd/@id"/>

        <xsl:element name="messageHeader">
            <xsl:element name="cmdType">
                <xsl:value-of select="$_commandType"/>
            </xsl:element>
        </xsl:element>

        <xsl:element name="messageBody">
            <xsl:choose>
                <xsl:when test="$_commandType = 'api_info'">
                    <xsl:element name="apiInfoBody">
                        <xsl:element name="apiVersion">
                            <xsl:value-of select="/message/cmd/api-version"/>
                        </xsl:element>
                        <xsl:element name="apiBuild">
                            <xsl:value-of select="/message/cmd/api-build"/>
                        </xsl:element>
                    </xsl:element>
                </xsl:when>
                <xsl:when test="$_commandType = 'communicationError'">
                    <xsl:element name="communicationErrorBody">
                        <xsl:element name="errorCode">
                            <xsl:value-of select="error-code"/>
                        </xsl:element>
                        <xsl:element name="badCmd">
                            <xsl:value-of select="bad-cmd"/>
                        </xsl:element>
                    </xsl:element>
                </xsl:when>
            </xsl:choose>
        </xsl:element>
    </xsl:template>
</xsl:stylesheet>

The change that fixed it was to add the exclude-result-prefixes attribute to the <xsl:stylesheet> element and to change section that was:

    <xsl:template match="message">
        <xsl:element name="message" xmlns="http://www.companyname.com/schemas/product/Version001">
            <xsl:apply-templates select="/message/cmd/@id"/>
        </xsl:element>
    </xsl:template>

to be the following instead:

    <xsl:template match="message">
        <message namespace="http://www.companyname.com/schemas/product/Version001">
            <xsl:apply-templates select="/message/cmd/@id"/>
        </message>
    </xsl:template>

And now I'm happy again.

There are probably better ways to do it, but this is working for me. Any future suggestions are still welcome.


Jim Garrison's (updated) answer is all you should have needed. But the updated stylesheet you posted outputs a "namespace" attribute in the result, so I'm not sure what good that does you.

The key thing to understand is how the default namespace works. Don't think of xmlns as an attribute that you can output to the result. Instead, think of it as a lexical detail of your stylesheet, whose purpose is to set the default namespace for everything beneath it (the element itself and every one of its descendants until overridden). (In turn, it has the same function in the result XML, which has a very different structure than the stylesheet itself.)

Literal result elements in your stylesheet (e.g., <message>) as well as <xsl:element> instructions (e.g., <xsl:element name="message">) both take on the default namespace when the name supplied doesn't use a prefix. If you want all of them to take on that same namespace, then you'll need to put a default namespace at the top of your stylesheet, as Jim Garrison suggested.

Otherwise, you'll end up with some elements that aren't in that namespace, which is why the result contained xmlns="" (unsetting the namespace for those elements).

0

精彩评论

暂无评论...
验证码 换一张
取 消