XSL output XML with no prefix without using the de

2019-04-12 00:56发布

问题:

I have an XSL where I need to generate output along the lines of this:

<moo xmlns="http://api.example.com">
    <foo>1358944586848</foo>
    <bar>
        <a>1</a>
        <b>2</b>
        <c>3</c>
    </bar>
</moo>

I could do it like this:

<xsl:stylesheet version="2.0"
    xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    xmlns="http://api.example.com">

    <xsl:template match="/">
        <xsl:element name="moo">
            <!-- and so on -->

However, I kind of hate using the xsl prefix in my xsl files cause I feel it clutters it up a lot. Selecting with XPath is easy anyways since you can set xpath-default-namespace to whatever you're transforming from if needed. But there is no element-default-namespace available as far as I can see, so how can I generate the wanted output in a good way?

I know I can do this:

<stylesheet version="2.0"
    xmlns="http://www.w3.org/1999/XSL/Transform">

    <template match="/">
        <element name="moo" namespace="http://api.example.com">
            <!-- and so on -->

But then I have to set this namespace explicitly on every single element I create, or they will end up with the XSL namespace instead. So is there a clean way to create elements with a certain namespace (without a prefix) and not touching the default namespace of the xsl file?


Update:

Figured maybe namespace-alias could do something, but can't figure out how to use it. Tried this, but doesn't seem to make any difference in the output at all:

<stylesheet version="2.0"
    xmlns="http://www.w3.org/1999/XSL/Transform"
xmlns:out="http://api.example.com">

<namespace-alias stylesheet-prefix="out" result-prefix=""/>

    <template match="/">
        <element name="out:moo">
            <!-- and so on -->

The namespace-alias thing probably isn't doing what I think it is :p

The final solution I used, based on JLRishe's answer

remove-prefixes.xsl

<?xml version="1.0" encoding="UTF-8"?>
<stylesheet version="2.0" xmlns="http://www.w3.org/1999/XSL/Transform">
    <template match="/">
        <variable name="result">
            <next-match />
        </variable>
        <apply-templates select="$result" mode="remove-prefixes" />
    </template>

    <template match="*" priority="1" mode="remove-prefixes">
        <element name="{local-name()}" namespace="{namespace-uri()}">
            <apply-templates select="@* | node()" mode="remove-prefixes" />
        </element>
    </template>
    <template match="@*|node()" mode="remove-prefixes">
        <copy>
            <apply-templates select="@* | node()" mode="remove-prefixes" />
        </copy>
    </template>

</stylesheet>

subject.xsl

<!-- snip -->
<import href="remove-prefixes.xsl" />
<!-- snip -->

回答1:

One thing you could do is capture the entire result of the XSL in a variable, and then blank out its prefixes at the end:

<stylesheet version="2.0" xmlns="http://www.w3.org/1999/XSL/Transform"
                          xmlns:p="http://api.example.com">
  <output method="xml" indent="yes"/>

  <template match="/">
    <variable name="result">
      <p:moo>
        <apply-templates />
      </p:moo>
    </variable>
    <apply-templates select="$result" mode="removePrefix" />
  </template>

  <template match="root">
    <p:something hello="hi">
      <element name="p:somethingelse" />
    </p:something>
  </template>

  <template match="p:*" mode="removePrefix">
    <element name="{local-name()}" namespace="{namespace-uri()}">
      <apply-templates select="@* | node()" mode="removePrefix" />
    </element>
  </template>

  <template match="@* | node()" mode="removePrefix">
    <copy>
      <apply-templates select="@* | node()" />
    </copy>
  </template>
</stylesheet>

When run on this input:

<root>
  <top />
</root>

it produces:

<moo xmlns="http://api.example.com">
  <something hello="hi">
    <somethingelse />
  </something>
</moo>


回答2:

The xmlns:xsl-approach is really the "standard" day, and I suppose that the designers of XSL had that in mind.

Remember that you're allowed to directly mix XML fragments into XSL. From that point of view, your approach with the xsl:element arguably adds much more noise and clutter than what you are trying to eliminate with your "element-default-namespace".

So, I'd do this:

<xsl:stylesheet version="2.0"
    xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    xmlns="http://api.example.com">

    <xsl:template match="/">
        <moo>
            <!-- and so on -->

Edit:

The following with namespace-alias might work:

<xsl:stylesheet version="2.0"
    xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    xmlns="http://api.example.com">

    <namespace-alias stylesheet-prefix="xsl" result-prefix="#default"/>

    <template match="/" xmlns="http://www.w3.org/1999/XSL/Transform">
        <element name="moo">
            <!-- etc -->

Or like this:

<stylesheet version="2.0"
    exclude-result-prefixes="api" 
    xmlns="http://www.w3.org/1999/XSL/Transform"
    xmlns:api="http://api.example.com">

    <namespace-alias stylesheet-prefix="#default" result-prefix="api"/>

    <template match="/">
        <element name="moo">
            <!-- etc -->


回答3:

I feel the same way as you about the cluttering, especially if I have stylesheets where the overwhelming majority of elements is in the XSL namespace, not in the target namespace. The solution is simple: Give the output elements a prefix rather than the XSL elements:

<stylesheet version="2.0"
    xmlns="http://www.w3.org/1999/XSL/Transform"
    xmlns:o="http://api.example.com">

    <template match="/">
        <o:moo>
            <!-- and so on -->