Вопрос

I have an xml document that is structured somewhat like this :-

<catalog xmlns="format_old" xmlns:final="format_new">
  <final:book>
    <final:title>blah</final:title>
    <final:author>more blah</final:author>
  </final:book>
  <book>
    <description title="blah2"/>
    <writer name="more blah2"/>
  </book>
</catalog>

Obviously, this is a simplified version of the problem. What I want to do is to convert this into something like :-

<catalog xmlns="format_new">
  <book>
    <title>blah</title>
    <author>more blah</author>
  </book>
  <book>
    <title>blah2</title>
    <author>more blah2</author>
  </book>
</catalog>

The stylesheet that I have right now is something like this :-

<xsl:stylesheet version="1.0"
  xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
  xmlns:orig="format_old"
  xmlns="format_new"/>

<xsl:template match="@*|node()">
  <xsl:copy>
    <xsl:apply-templates select="@*|node()" />
  </xsl:copy>
</xsl:template>

<xsl:template match="//orig:book">
  <xsl:element name="title">
    <xsl:value-of select="./orig:description/@title" />
  </xsl:element>
  <xsl:element name="author">
    <xsl:value-of select="./orig:writer/@name" />
  </xsl:element>
</xsl:template>

</xsl:stylesheet>

This gives me an output like :-

<catalog xmlns="format_old">
  <book xmlns="format_new">
    <title>blah</title>
    <author>more blah</author>
  </book>
  <book xmlns:orig="format_old" xmlns="format_new">
    <title>blah2</title>
    </author>more blah2</author>
  </book>
</catalog>

There are two problems with this stylesheet :-

1.) (major issue) The root element gets copied over rather than changing the default namespace of the root element. So basically the catalog element would still be in the namespace format_old.

2.) (minor issue) This will convert the elements as :-

<book xmlns:orig="format_old" xmlns="format_new">
  ...
</book>

instead of picking up the namespace from the root element as keeping it as

<book>
  ...
</book>

What am I missing here? I'm using Xalan-C.

Это было полезно?

Решение

I think the following should do:

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0"
    xmlns="format_new"
    xmlns:ns1="format_old"
    exclude-result-prefixes="ns1"
    xmlns:xsl="http://www.w3.org/1999/XSL/Transform">

<xsl:template match="@* | text() | comment() | processing-instruction()">
  <xsl:copy/>
</xsl:template>

<xsl:template match="*">
  <xsl:element name="{local-name()}">
    <xsl:apply-templates select="@* | node()"/>
  </xsl:element>
</xsl:template>

<xsl:template match="ns1:book/ns1:description[@title]">
  <title>
    <xsl:value-of select="@title"/>
  </title>
</xsl:template>

<xsl:template match="ns1:book/ns1:writer[@name]">
  <author>
    <xsl:value-of select="@name"/>
  </author>
</xsl:template>

</xsl:stylesheet>

Saxon 6.5.5 transforms your input into

<?xml version="1.0" encoding="utf-8"?><catalog xmlns="format_new">
  <book>
    <title>blah</title>
    <author>more blah</author>
  </book>
  <book>
    <title>blah2</title>
    <author>more blah2</author>
  </book>
</catalog>

Другие советы

You're close. Your default templates are picking up everything you don't have other templates for.

Your first problem is that they are picking up the orig:catalog element and writing it out unchanged, which turns out not to be what you want. Simple fix: add a template for it.

Your second problem is managing the namespace declarations in your output. Here, several techniques can be helpful:

  • Read carefully the documentation for xsl:exclude-result-prefixes in the spec or in your favorite XSLT reference; use it to tell your processor that you don't need to have the namespace declaration for the old namespace around.

  • Use the xsl:element constructor instead of literal result elements, if you want to exploit the fact that the output from literal result elements always carries all the inscope namespace prefixes found on the LRE in the stylesheet. See this SO question for a few more details.

  • Write a simple filter in SAX or your favorite editor to give yourself complete control over where namespaces are declared, and how. (XSLT's design takes the view that you should worry about namespace declarations much, with the result that it's hard to control them very well.)

  • Train yourself not to care too much if your output has some extraneous namespace declarations, and write your downstream consumers to do the right thing as long as everything is bound correctly, so they are not bothered by extraneous namespace declarations.

Different people have different levels of success with these different techniques; myself, I find the last one particularly effective, and I only worry about the other ones when it breaks down on me.

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top