How to copy parent node namespace to child element using xslt? - java

My xml looks like which I created using Java JAXBContext and Marshaller.
I want to format some part of xml only not the whole xml.
<?xml version="1.0" encoding="UTF-8"?>
<ns4:Requests xmlns:ns2="http://www.dummy.com/xsd/tublu/murmur_001" xmlns:ns3="http://www.dummy.com/xsd/CommonObjects_001" xmlns:ns4="http://www.dummy.com/xsd/naku_001">
<ns4:RequestSetId>fhskgvseruigiu</ns4:RequestSetId>
<ns4:RequestStream>CHAPP</ns4:RequestStream>
<ns4:Request>
<ns4:TrackAndTrace>
<ns4:CPAId>003</ns4:CPAId>
<ns4:CorrelationId>ytuty</ns4:CorrelationId>
</ns4:TrackAndTrace>
</ns4:Request>
<ns4:Request>
<ns4:TrackAndTrace>
<ns4:CPAId>003</ns4:CPAId>
<ns4:CorrelationId>cyuri7</ns4:CorrelationId>
</ns4:TrackAndTrace>
</ns4:Request>
</ns4:Requests>
I want to format like
<?xml version="1.0" encoding="UTF-8"?>
<ns4:Requests xmlns:ns2="http://www.dummy.com/xsd/tublu/murmur_001" xmlns:ns4="http://www.dummy.com/xsd/naku_001" xmlns:ns3="http://www.dummy.com/xsd/CommonObjects_001">
<ns4:RequestSetId>fhskgvseruigiu</ns4:RequestSetId>
<ns4:RequestStream>CHAPP</ns4:RequestStream>
<ns4:Request xmlns:ns4="http://www.dummy.com/xsd/naku_001"><ns4:TrackAndTrace><ns4:CPAId>003</ns4:CPAId><ns4:CorrelationId>ytuty</ns4:CorrelationId></ns4:TrackAndTrace></ns4:Request>
<ns4:Request xmlns:ns4="http://www.dummy.com/xsd/naku_001"><ns4:TrackAndTrace><ns4:CPAId>003</ns4:CPAId><ns4:CorrelationId>cyuri7</ns4:CorrelationId></ns4:TrackAndTrace></ns4:Request>
</ns4:Requests>

Here is the solution (by Transforming the XML Data using Java's XSLT APIs),
As you may also have noticed.. JAXB alone cannot meet this requirement, but after marshalling the object to a formatted XML String (as u have shown) you can then post process/transform it accordingly using a suitable XSLT file
So to get a linearized 'Request' element, just make use of the xsl shown below:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:strip-space elements="TrackAndTrace"/>
<xsl:strip-space elements="Request"/>
<xsl:template match="#*|node()">
<xsl:copy>
<xsl:apply-templates select="#*|node()"/>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
Note: Also tested that above method/approach is working properly - used the Stylizer sample code (from https://docs.oracle.com/javase/tutorial/jaxp/xslt/transformingXML.html)
Cheers!
Update: If you want a solution that also preserves the original namespace prefix as shown in your question, follow this variation
Add factory.setNamespaceAware(true); in the Stylizer code
& Use this tweaked XSLT
<xsl:stylesheet version="1.0" xmlns:xsl="w3.org/1999/XSL/Transform" xmlns:ns4="dummy.com/xsd/naku_001">
<xsl:strip-space elements="ns4:TrackAndTrace"/>
<xsl:strip-space elements="ns4:Request"/>
<xsl:template match="#*|node()">
<xsl:copy>
<xsl:apply-templates select="#*|node()"/>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>

Related

XSLT to pick specific nodes, and parameterizing it from Java

I have an XML structure like this,
<?xml version="1.0" encoding="UTF-8"?>
<Package>
<PackageHeader>yadda yadda </PackageHeader>
<PackageBody>
<Element1>foo</Element1>
<Element2>bar</Element2>
<ElementN>xyz</ElementN>
</PackageBody>
I have a requirement where I need to eliminate either Element1, Element2, or ElementN, so I wrote this XSLT,
<xsl:stylesheet version="3.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" indent="yes" />
<xsl:template match="#* | node()">
<xsl:copy>
<xsl:apply-templates select="#* | node()[not(self::Element1)][not(self::Element2)]" />
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
I am running this via a simple Java XSL Transformation program. The transformed XML has only elevrything from the orignal XML minus Element1 & Element2. I tried many ways to pass parameters from the Java program to parameterize which nodes should be eliminated, but no luck so far. Any help would be much appreciated.
Sounds like a task for XSLT 3 with static parameters and shadow attributes, best used with the Saxon s9api (http://saxonica.com/html/documentation9.9/javadoc/index.html) if that is the processor used:
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
exclude-result-prefixes="#all"
version="3.0">
<xsl:param name="element-to-be-removed" static="yes" as="xs:string" select="'Element2'"/>
<xsl:mode on-no-match="shallow-copy"/>
<xsl:template _match="{$element-to-be-removed}"/>
</xsl:stylesheet>
https://xsltfiddle.liberty-development.net/gVhEaiE

How to update a range of an XMl file tag?

My problem is I don't know How to update a XML file. In the following XML file I want to include some tags inside another tag which are already exist in the file.
**My XML file is as following: **
<?xml version="1.0" encoding="UTF-8"?>
<root>
<PayrunDetails>
<PayrunNumber>000777</PayrunNumber>
</PayrunDetails>
<PayLocation>
<LocationCode>ACT</LocationCode>
<LocationDescription>ACT</LocationDescription>
<CompanyDetails>
<CName>APPLE Limited</CName>
<Payslip>
<StaffNumber>12345</StaffNumber>
<PayDetails>
<AmountGross>9999</AmountGross>
<ComponentDetails>
<ComponentType>SALARY</ComponentType>
<Amount>1999</Amount>
<YTDAmount>10616</YTDAmount>
</ComponentDetails>
<ComponentDetails>
<ComponentType>SALARY</ComponentType>
<Amount>7305</Amount>
<YTDAmount>76703</YTDAmount>
</ComponentDetails>
</PayDetails>
</Payslip>
</CompanyDetails>
</PayLocation>
</root>
My desired output file is as following:
<?xml version="1.0" encoding="UTF-8"?>
<root>
<PayrunDetails>
<PayrunNumber>000777</PayrunNumber>
</PayrunDetails>
<PayLocation>
<LocationCode>ACT</LocationCode>
<LocationDescription>ACT</LocationDescription>
<CompanyDetails>
<CName>APPLE Limited</CName>
<Payslip>
<StaffNumber>12345</StaffNumber>
<PayDetails>
<AmountGross>9999</AmountGross>
<ComponentDetails>
<ComponentType ID="SALARY">
<Amount>1999</Amount>
<YTDAmount>10616</YTDAmount>
</ComponentType>
</ComponentDetails>
<ComponentDetails>
<ComponentType ID="TAX">
<Amount>7305</Amount>
<YTDAmount>76703</YTDAmount>
</ComponentType>
</ComponentDetails>
</PayDetails>
</Payslip>
</CompanyDetails>
</PayLocation>
</root>
In the above desired file you will find that ComponentType tag has included the rest of the tags exist inside the ComponentDetails tag.
For the above said problem I want to use XSLT but I don't know what code should I write to get the solution.
I'm fairly new to XSLT so please excuse the potential novice question. Any guidance would be appreciated here.
Thanks in advance.
First read up on the identity transform in XSLT, which involves this template
<xsl:template match="#*|node()">
<xsl:copy>
<xsl:apply-templates select="#*|node()"/>
</xsl:copy>
</xsl:template>
(If you could use XSLT 3.0, you could just write <xsl:mode on-no-match="shallow-copy"/> instead)
This will copy across all your nodes and attributes as-is, which in your case gets you almost there.
There are a number of ways you could the transform of the nodes you want. One way is to match the ComponentDetails tag, to create a new ComponentType in the output, along with code to select the other child nodes.
<xsl:template match="ComponentDetails">
<xsl:copy>
<ComponentType ID="{ComponentType}">
<xsl:apply-templates />
</ComponentType>
</xsl:copy>
</xsl:template>
This makes use of Attribute Value Templates to create the ID attribute.
Note that <xsl:apply-templates /> is short-hand for <xsl:apply-templates select="node()" /> and so this will still select the existing ComponentType element in the input document, which will then be matched by the identity template. To stop ComponentType being output twice, you need to add a template to match and ignore it.
<xsl:template match="ComponentType" />
Try this XSLT
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
<xsl:output method="xml" indent="yes" html-version="5"/>
<xsl:strip-space elements="*" />
<xsl:template match="#*|node()">
<xsl:copy>
<xsl:apply-templates select="#*|node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="ComponentDetails">
<xsl:copy>
<ComponentType ID="{ComponentType}">
<xsl:apply-templates />
</ComponentType>
</xsl:copy>
</xsl:template>
<xsl:template match="ComponentType" />
</xsl:stylesheet>

add namespace and prefix to xml

I am using camel to route messages to a Webservice. The Messages are like XML but without namespaces/prefixes. The problem now is that the Webservice expects the XML but with the appropriate namespaces for each element. So as an example:
<a>
<b>value_b</b>
<c>value_c</c>
</a>
is what im getting in, but what needs to be sent out should look like this
<a xmlns:n1="http://yadda-ns1.com" xmlns:n2="http://yadda-ns2.com">
<ns1:b>value_b</ns1:b>
<ns2:c>value_c</ns2:c>
</a>
if it was the same namespace on all elements i would have just used an xslt to add it. But its mostly 2 or 3 different namespaces.
Now is it possible to add the namespaces in my camel route? I had the idea to use jaxb to marshal from the "incomplete" XML to the "complete" one (with XML), would this work? I was trying this but did not succeed yet.
Or does someone have a different idea? What i also have in my project is the XSDs and JAXB Annotated Classes so these can also be used and the messages are identical apart from the missing namespace.
Best Regards
Thomas
You could transform the XML with a stylesheet such as the one below to modify the elements to be bound to the appropriate namespaces:
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0"
xmlns:ns1="http:yadayada-ns1.com"
xmlns:ns2="http:yadayada-ns2.com">
<xsl:output indent="yes"/>
<!-- identity template that copies content(unless more specific templates match) -->
<xsl:template match="#*|node()">
<xsl:copy>
<xsl:apply-templates select="#*|node()"/>
</xsl:copy>
</xsl:template>
<!-- Make the "a" elements in the ns1 namespace -->
<xsl:template match="a">
<xsl:element name="ns1:{local-name()}">
<xsl:apply-templates select="#*|node()"/>
</xsl:element>
</xsl:template>
<!-- Make the "b" and "c" elements in the ns2 namespace -->
<xsl:template match="b|c">
<xsl:element name="ns2:{local-name()}">
<xsl:apply-templates select="#*|node()"/>
</xsl:element>
</xsl:template>
</xsl:stylesheet>

XSLT dont add XMLNS to elements

I have a simple HTML fragment similar to this:
link
I need to transform it to
<abc:href var="123">link</abc:href>
I do it with XSLT, so I had to add the namespace in xsl:stylesheet
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:abc="http://abc.ru">
It works almost fine, unfortunately the XSLT transform keeps on adding a XMLNS to the output, like here:
<abc:href var="123" xmlns:abc="http://abc.ru">link</abc:href>
I don't need the xmlns definition, can I remove it?
Although it really goes against the grain, and I advise strongly against it, if you need to produce this malformed XML, then you can use an instruction like...
<xsl:value-of disable-output-escaping="yes" select="
concat('<abc:href var="',$href,'">',$link,'</abc:href>')
"/>
... where $href and $link are place-markers for the appropriate expression.
Update
In response to the OP's comment, one could use a template like this...
<xsl:template match="a">
<xsl:value-of disable-output-escaping="yes" select="
concat('<abc:href var="',#href,'">',.,'</abc:href>')
"/>
</xsl:template>
This ugly solution should be used only as a last resort. A much better solution would be to use XSLT to produce your WHOLE document, not just an invalid fragment of it. This way you document would be well formed and you could bring to bear the full power and simplicity of XSLT.
It works almost fine, unfortunately the XSLT transform keeps on adding
a XMLNS to the output, like here:
<abc:href var="123" xmlns:abc="http://abc.ru">link</abc:href>
I don't need the xmlns definition, can I remove it?
The wanted removal of the namespace declaration would produce a (namespace-)non-well-formed XML document and for this reason the XSLT processor adds the namespace declaration -- as required by the W3C XSLT specifications.
You can cause these namespace declarations to "disappear" by placing the namespace declaration on a common ancestor (such as the top element of the generated XML document).
Here is a complete example:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output omit-xml-declaration="yes" indent="yes"/>
<xsl:strip-space elements="*"/>
<xsl:template match="/*">
<top xmlns:abc="http://abc.ru">
<xsl:apply-templates/>
</top>
</xsl:template>
<xsl:template match="a[#href]">
<xsl:element name="abc:href" namespace="http://abc.ru">
<xsl:attribute name="var">
<xsl:apply-templates/>
</xsl:attribute>
</xsl:element>
</xsl:template>
</xsl:stylesheet>
When this transformation is applied on the following document:
<html>
link1
link2
link3
link4
</html>
the wanted, correct result is produced:
<top xmlns:abc="http://abc.ru">
<abc:href var="link1"/>
<abc:href var="link2"/>
<abc:href var="link3"/>
<abc:href var="link4"/>
</top>
This is sad, but I really need an invalid xml
XSLT is designed to prevent you producing bad XML. If you want to produce bad XML, don't use XSLT.
Try it with exclude-result-prefixes, like this:
<?xml version="1.0" encoding="ISO-8859-1"?>
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:abc="http://abc.ru"
exclude-result-prefixes="abc">
<xsl:template match="/">
<xsl:apply-templates select="#* | node()"/>
</xsl:template>
<xsl:template match="a">
<href var="{#href}"><xsl:value-of select="."/></href>
</xsl:template>
</xsl:stylesheet>

Create xmlns attribute in the XML using XSLT Transformation

I am trying to add the xmlns attribute to the resulting XML with a value passed by parameter during XSLT transformation using JDK Transformer (Oracle XML v2 Parser or JAXP) but it always defaults to http://www.w3.org/2000/xmlns/
My source XML
<test/>
My XSLT
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns="http://example.com">
<xsl:param name="myNameSpace" select="'http://neilghosh.com'"/>
<xsl:template match="/">
<process>
<xsl:attribute name="xmlns:neil">
<xsl:value-of select="$myNameSpace"/>
</xsl:attribute>
</process>
</xsl:template>
</xsl:stylesheet>
My Result
<?xml version="1.0"?>
<process xmlns="http://www.w3.org/2000/xmlns/" xmlns:neil="neilghosh.com">
</process>
My Desired Result
<?xml version="1.0"?>
<process xmlns="http://example.com" xmlns:neil="neilghosh.com">
</process>
Firstly, in the XSLT data model, you don't want to create an attribute node, you want to create a namespace node.
Namespace nodes are usually created automatically: if you create an element or attribute in a particular namespace, the requisite namespace node (and hence, when serialized, the namespace declaration) are added automatically by the processor.
If you want to create a namespace node that isn't necessary (because it's not used in the name of any element or attribute) then in XSLT 2.0 you can use xsl:namespace. If you're stuck with XSLT 1.0 then there's a workaround, that involves creating an element in the relevant namespace and then copying its namespace node:
<xsl:variable name="ns">
<xsl:element name="neil:dummy" namespace="{$param}"/>
</xsl:variable>
<process>
<xsl:copy-of select="$ns/*/namespace::neil"/>
</process>
Michael Kay provided you with the correct answer, but based on your comments, you aren't sure how to use it in your transformation.
Here is a complete transformation:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:ext="http://exslt.org/common" exclude-result-prefixes="ext">
<xsl:output omit-xml-declaration="yes" indent="yes"/>
<xsl:param name="pNamespace" select="'neilghosh.com'"/>
<xsl:variable name="vDummy">
<xsl:element name="neil:x" namespace="{$pNamespace}"/>
</xsl:variable>
<xsl:template match="/*">
<xsl:element name="process" namespace="http://example.com">
<xsl:copy-of select="namespace::*"/>
<xsl:copy-of select="ext:node-set($vDummy)/*/namespace::*[.=$pNamespace]"/>
</xsl:element>
</xsl:template>
</xsl:stylesheet>
When this transformation is applied on the provided XML document:
<test/>
the wanted, correct result is produced:
<process xmlns="http://example.com" xmlns:neil="neilghosh.com" />
Namespace declarations in XML are not attributes even though they look like attributes. In XSLT 2.0 you can use <xsl:namespace name="neil" select="$myNameSpace" /> to add a namespace declaration to the result tree dynamically but that feature is not available in XSLT 1.0.
Don't try to create "xmlns" attributes yourself. Create the namespaces in the XSLT and they will be done automatically.
This XSLT works (tested with Saxon 9.4):
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:neil="neilghosh.com"
xpath-default-namespace="http://example.com"
xmlns="http://example.com" version="2.0">
<xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>
<xsl:param name="myDynamicNamespace" select="'http://neilghosh.com'"/>
<xsl:template match="/">
<xsl:element name="process">
<xsl:namespace name="neil" select="$myDynamicNamespace"/>
</xsl:element>
</xsl:template>
</xsl:stylesheet>
And gives the following output:
<?xml version="1.0" encoding="UTF-8"?>
<process xmlns="http://example.com" xmlns:neil="http://neilghosh.com"/>
Finally got an workaround which worked with my XSLT Processor (Oracle XML V2 Parser)
I had to transform it to a DOM Document and then persist that DOM to filesystem instead of outputting directly to StreamResult
I used DOMResult in the transform method
Following XSLT fragment worked but there was an extra xmlns:xmlns="http://www.w3.org/2000/xmlns/" which was probably absorbed by Document and did not appear in the final output when I persisted to file system.
<process>
<xsl:attribute name="xmlns">
<xsl:value-of select="'http://example.com'"/>
</xsl:attribute>
<process>
I know this is not the best way to do but given the parse constraint this is the only choice I have now.

Categories