I need to transform a given XML to another format. This is the source:
<?xml version="1.0" encoding="utf-8"?>
<package xmlns="http://apple.com/itunes/importer" version="film5.1">
<provider>Provider</provider>
<language>de-DE</language>
<video>
<type>film</type>
<subtype>feature</subtype>
<vendor_id>some_id</vendor_id>
<country>US</country>
<original_spoken_locale>en</original_spoken_locale>
<title>Some movie title</title>
</video>
</package>
And this is the XSLT I tried:
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:importer="http://apple.com/itunes/importer" version="1.0">
<xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>
<xsl:template match="importer:package">
<xsl:variable name="var-title">
<xsl:apply-templates select="video/title"/>
</xsl:variable>
<Movie>
<Title><xsl:value-of select="$var-title"/></Title>
</Movie>
</xsl:template>
</xsl:stylesheet>
But the <title> from source XML is not selected. What did I do wrong?
The namespace applies to the descendants as well so change <xsl:apply-templates select="video/title"/> to <xsl:apply-templates select="importer:video/importer:title"/> to use a prefix as well.
Related
I have trouble in transform part of a xml to a new xml without namespace.
Input xml is:
<?xml version="1.0" encoding="ISO-8859-1" standalone="no"?>
<node1 xmlns="http://a.com">
<ServiceData>
<b:test xmlns:b="http://b.com">
<b:somtag>
</b:somtag>
</b:test>
</ServiceData>
</node1>
and what I want is:
<a>
<c>
<ServiceData>
<b:test xmlns:b="http://b.com">
<b:somtag>
</b:somtag>
</b:test>
</ServiceData>
</c>
</a>
The format I want is with no namespace for ServiceData.
Any help is appreciated, thanks.
Added, I tried to use this xsl, but I can't remove "xmlns="http://a.com""
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:test="http://a.com" exclude-result-prefixes="test">
<xsl:output method="xml" encoding="UTF-8" omit-xml-declaration="yes" />
<xsl:template match="/">
<a><c><ServiceData><xsl:copy-of select="//test:ServiceData/*"/></ServiceData></c></a>
</xsl:template>
</xsl:stylesheet>
The result I got is:
<a>
<c>
<ServiceData>
<b:test xmlns:b="http://b.com" xmlns="http://a.com">
<b:somtag>
</b:somtag>
</b:test>
</ServiceData>
</c>
</a>
How about:
XSLT 2.0
<xsl:stylesheet version="2.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xpath-default-namespace="http://a.com">
<xsl:output method="xml" encoding="UTF-8" indent="yes" />
<xsl:template match="/node1">
<a>
<c>
<ServiceData>
<xsl:copy-of select="ServiceData/*" copy-namespaces="no" />
</ServiceData>
</c>
</a>
</xsl:template>
</xsl:stylesheet>
Added:
I assumed you could use an XSLT 2.0 processor, because your stylesheet says version="2.0". If that's not true, then you cannot use xsl:copy-of; instead, you must reconstruct the elements with their original names and namespaces:
XSLT 1.0
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:test="http://a.com"
exclude-result-prefixes="test">
<xsl:output method="xml" version="1.0" encoding="utf-8" indent="yes"/>
<xsl:template match="/test:node1">
<a>
<c>
<ServiceData>
<xsl:apply-templates select="test:ServiceData/*"/>
</ServiceData>
</c>
</a>
</xsl:template>
<xsl:template match="*">
<xsl:element name="{name()}" namespace="{namespace-uri()}">
<xsl:apply-templates/>
</xsl:element>
</xsl:template>
</xsl:stylesheet>
Note:
A redundant namespace declaration should not make any difference to the receiving application. The results received here are semantically identical to the result you show in your question.
I have many XML files that I want to process with XSLT. I want the result to include custom CSS for the purpose of displaying the files a distinct way in Oxygen’s Author mode.
Input:
<?xml version="1.0" encoding="utf-8"?>
<alto xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.loc.gov/standards/alto/ns-v2# http://www.loc.gov/standards/alto/alto-v2.0.xsd" xmlns="http://www.loc.gov/standards/alto/ns-v2#">
<!—more XML-->
</alto>
XSL:
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
xpath-default-namespace="http://www.loc.gov/standards/alto/ns-v2#"
exclude-result-prefixes="xs"
version="2.0">
<xsl:output indent="yes"/>
<xsl:strip-space elements="*"/>
<!—A series of templates that transform the XML-->
</xsl:stylesheet>
Desired Output:
<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet href="my-style.css"?>
<alto xmlns:xlink="http://www.w3.org/1999/xlink"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://www.loc.gov/standards/alto/ns-v2#"
xsi:schemaLocation="http://www.loc.gov/standards/alto/ns-v2# http://www.loc.gov/standards/alto/alto-v2.0.xsd">
<!—more XML-->
</alto>
What do I need to add to my stylesheet to get the declaration to display in each XML file?
Use the xsl:processing-instruction instruction.
So your stylesheet could look like this:
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
xpath-default-namespace="http://www.loc.gov/standards/alto/ns-v2#"
exclude-result-prefixes="xs"
version="2.0">
<xsl:output indent="yes"/>
<xsl:strip-space elements="*"/>
<xsl:template match="/">
<xsl:processing-instruction name="xml-stylesheet">href="my-style.css"</xsl:processing-instruction>
<xsl:apply-templates select="node()|#*" />
</xsl:template>
<!-- Identity template -->
<xsl:template match="node()|#*">
<xsl:copy>
<xsl:apply-templates select="node()|#*" />
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
Output is:
<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet href="my-style.css"?>
<alto xmlns:xlink="http://www.w3.org/1999/xlink"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://www.loc.gov/standards/alto/ns-v2#"
xsi:schemaLocation="http://www.loc.gov/standards/alto/ns-v2# http://www.loc.gov/standards/alto/alto-v2.0.xsd"/>
Add
<xsl:template match="/">
<xsl:processing-instruction name="xml-stylesheet">href="my-style.css"</xsl:processing-instruction>
<xsl:next-match/>
</xsl:template>
at the end of the stylesheet or make sure you edit the template you have for match="/" and insert the <xsl:processing-instruction name="xml-stylesheet">href="my-style.css"</xsl:processing-instruction> there.
I have XML
<getInquiryAboutListReturn xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<inquiryAbouts>
<inquiryAbout>
<code>Code</code>
<nameKk>Something</nameKk>
<nameRu>Something</nameRu>
<documents xsi:nil="true"/>
</inquiryAbout>
</inquiryAbouts>
</getInquiryAboutListReturn>
And I want to process it with XSLT to copy all XML
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
<xsl:output indent="yes" />
<xsl:template match="/">
<xsl:copy-of select="//getInquiryAboutListReturn/inquiryAbouts"/>
</xsl:template>
</xsl:stylesheet>
How could I copy all XML without <documents xsi:nil="true"/> or without xsi:nil="true"?
Desired output XML
<getInquiryAboutListReturn xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<inquiryAbouts>
<inquiryAbout>
<code>Code</code>
<nameKk>Something</nameKk>
<nameRu>Something</nameRu>
</inquiryAbout>
</inquiryAbouts>
</getInquiryAboutListReturn>
This simple XSLT:
<?xml version="1.0"?>
<xsl:stylesheet
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
version="1.0">
<xsl:output omit-xml-declaration="no" indent="yes"/>
<xsl:strip-space elements="*"/>
<!-- TEMPLATE #1 -->
<xsl:template match="node()|#*">
<xsl:copy>
<xsl:apply-templates select="node()|#*"/>
</xsl:copy>
</xsl:template>
<!-- TEMPLATE #2 -->
<xsl:template match="*[#xsi:nil = 'true']" />
</xsl:stylesheet>
...when applied to the OP's source XML:
<?xml version="1.0"?>
<getInquiryAboutListReturn xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<inquiryAbouts>
<inquiryAbout>
<code>Code</code>
<nameKk>Something</nameKk>
<nameRu>Something</nameRu>
<documents xsi:nil="true"/>
</inquiryAbout>
</inquiryAbouts>
</getInquiryAboutListReturn>
...produces the expected result XML:
<?xml version="1.0"?>
<getInquiryAboutListReturn xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<inquiryAbouts>
<inquiryAbout>
<code>Code</code>
<nameKk>Something</nameKk>
<nameRu>Something</nameRu>
</inquiryAbout>
</inquiryAbouts>
</getInquiryAboutListReturn>
EXPLANATION:
The first template -- the Identity Template -- copies all nodes and attributes from the source XML document as-is.
The second template, which matches all elements with the specified, namespaced attribute equalling "true", effectively removes those elements.
Using Visual Studio to perform the transform during development the resulting xml contains text from the source xml in the destination that was contained in tags that do not match my template criteria
I was expecting that my select Group in the first template to find any elements named Group that are immediate children of CrystalReport and pass them along in the apply template call. I understood that the match filter on my second template would only take in Group's that have an attribute of Level=1 and write them out. I'd expect everything else to be ignored.
Why does "not this" appear in my output?
source
<?xml version="1.0" encoding="utf-8" ?>
<!--
UPDATE: Note that adding the xmlns attribute causes all output
to disappear unless you use Chris's second solution.
-->
<CrystalReport xmlns="urn:crystal-reports:schemas:report-detail" >
<Group Level="1">
<GroupHeader>
<Section>
<Field FieldName="apple" />
</Section>
</GroupHeader>
<Group Level="2">
not this
</Group>
</Group>
</CrystalReport>
transform
<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" indent="yes"/>
<xsl:strip-space elements="*"/>
<xsl:template match="/CrystalReport">
<root>
<xsl:apply-templates select="Group"/>
</root>
</xsl:template>
<xsl:template match="Group[#Level='1']/GroupHeader">
<tag1><xsl:value-of select="Section/Field/#FieldName"/></tag1>
</xsl:template>
</xsl:stylesheet>
output
<?xml version="1.0" encoding="utf-8"?>
<root>
<tag1>apple</tag1>
not this
</root>
You are facing the problem where the built in templates of the XML parser are coming into play. You are apply templates to all of the Group elements, but only catching one of them with your own templates. The other is handled by the default templates which out put the values of all nodes. I suggest that you change
<xsl:template match="/CrystalReport">
into
<xsl:template match="/">
This will override the root default templates which are producing the extra output. You can find more on the built in template rules at http://www.w3.org/TR/xslt#built-in-rule
Then override the basic text() template so you final XSLT looks a bit like this
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" indent="yes"/>
<xsl:strip-space elements="*"/>
<xsl:template match="/">
<root>
<xsl:apply-templates select="CrystalReport/Group"/>
</root>
</xsl:template>
<xsl:template match="Group[#Level='1']/GroupHeader">
<tag1>
<xsl:value-of select="Section/Field/#FieldName"/>
</tag1>
</xsl:template>
<xsl:template match="text()"/>
UPDATE
Or even simpler you could just match the desired elements and use something like this
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" indent="yes"/>
<xsl:strip-space elements="*"/>
<xsl:template match="/CrystalReport">
<root>
<xsl:apply-templates select="Group[#Level='1']/GroupHeader"/>
</root>
</xsl:template>
<xsl:template match="GroupHeader">
<tag1>
<xsl:value-of select="Section/Field/#FieldName"/>
</tag1>
</xsl:template>
This will leave the default text() templates in place which can be very handy.
Try
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:crystal="urn:crystal-reports:schemas:report-detail">
<xsl:output method="xml" indent="yes"/>
<xsl:strip-space elements="*"/>
<xsl:template match="/crystal:CrystalReport">
<root>
<xsl:apply-templates select="crystal:Group[#Level='1']/crystal:GroupHeader"/>
</root>
</xsl:template>
<xsl:template match="crystal:GroupHeader">
<tag1>
<xsl:value-of select="crystal:Section/crystal:Field/#FieldName"/>
</tag1>
</xsl:template>
Here's a trivial but valid Docbook article:
<?xml version="1.0" encoding="utf-8"?>
<article xmlns="http://docbook.org/ns/docbook" version="5.0">
<title>I Am Title</title>
<para>I am content.</para>
</article>
Here's a stylesheet that selects title if I remove the xmlns attribute above, and not if I leave it in:
<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="html"/>
<xsl:template match="/">
<xsl:apply-templates select="article"/>
</xsl:template>
<xsl:template match="article">
<p><xsl:value-of select="title"/></p>
</xsl:template>
<xsl:template match="text()"/>
</xsl:stylesheet>
How do I talk XPath into selecting title through article if it has that namespace attribute?
You need to add an alias for your namespace and use that alias in your XPath
<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:a="http://docbook.org/ns/docbook"
exclude-result-prefixes="a"
>
<xsl:output method="html"/>
<xsl:template match="/">
<xsl:apply-templates select="a:article"/>
</xsl:template>
<xsl:template match="a:article">
<p><xsl:value-of select="a:title"/></p>
</xsl:template>
<xsl:template match="text()"/>
</xsl:stylesheet>