We are building a web service which takes an XML input and provides and XML response based on the input. The input XML contains the XML structure with empty value, the empty values will be replaced by the actual value based on the data in the database. The user can reduce the number of nodes requested in the input XML, if they do not want all the details.
Sample Input XML
<?xml version="1.0" encoding="UTF-8"?>
<XML>
<RequestHeader>
<id>123</id>
</RequestHeader>
<RequestedElement>
<ABC>
<DEF>
<GHI />
<JKL>
<MNO />
</JKL>
</DEF>
</ABC>
</RequestedElement>
</XML>
Sample Response
<XML>
<ABC>
<DEF>
<GHI>Value1</GHI>
<JKL>
<MNO>Value2</MNO>
</JKL>
</DEF>
</ABC>
</XML>
The way I have implemented now, is to having a mapping of the XML nodes to the table names and column names, and then use reflection to retrieve the data from the database and generate the XML. However using reflection is slowing down the whole process.
The other option which I can think of is to get rid of reflection and to create the XML with all the nodes, and then use XSLT to generate the final XML with only the requested nodes. Is this possible to do so with XSLT?
Is there any better option to do the same which can increase the performance and get the desired results?
We are building a web service which takes an XML input and provides
and XML response based on the input. The input XML contains the XML
structure with empty value, the empty values will be replaced by the
actual value based on the data in the database. The user can reduce
the number of nodes requested in the input XML, if they do not want
all the details
One way to do this is to invoke the chosen XSLT processor (how to do this differs from one XSLT processor to another -- read the documentation) passing to it global transformation parameters, whose names are the names of the elements that should receive the corresponding values.
In the transformation below the values of the parameters are hardcoded, but in the real implementation these must be provided by the invoker -- so in the XSLT code they will look like this:
<xsl:param name="GHI"/>
<xsl:param name="MNO"/>
Here is the complete transformation:
<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:param name="GHI">Value1</xsl:param>
<xsl:param name="MNO">Value2</xsl:param>
<xsl:template match="node()|#*">
<xsl:copy>
<xsl:apply-templates select="node()|#*"/>
</xsl:copy>
</xsl:template>
<xsl:template match="RequestHeader"/>
<xsl:template match="*[not(node()) and name() = document('')/*/xsl:param/#name]">
<xsl:copy>
<xsl:value-of select="document('')/*/xsl:param[#name = name(current())]"/>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
When this transformation is applied on the provided XML document:
<XML>
<RequestHeader>
<id>123</id>
</RequestHeader>
<RequestedElement>
<ABC>
<DEF>
<GHI />
<JKL>
<MNO />
</JKL>
</DEF>
</ABC>
</RequestedElement>
</XML>
the wanted result is produced:
<XML>
<RequestedElement>
<ABC>
<DEF>
<GHI>Value1</GHI>
<JKL>
<MNO>Value2</MNO>
</JKL>
</DEF>
</ABC>
</RequestedElement>
</XML>
Related
I was trying to do a simple string delete in XML.
I want to delete something like the following.
<A>
<B>Test Name</B>
</A>
Has to work with all possible XML, though.
<Test><A><B>Test Name</B></A></Test>
<Test ><A ><B >Test Name</B ></A ></Test >
<Test>
<A>
<B>Test Name</B>
</A>
</Test>
etc, etc.
The regularEX I got so far, is simply:
<A>\s*(\r\n|\r|\n)*\s*<B>Test Name<\/B>\s*(\r\n|\r|\n)*\s*<\/A>
Everyone always says regEx is bad for match XML, which it clearly is. So what should I use instead.
GC_
The best approach for this case would be using XSLT. And even with XSLT-1.0 this is simple (You can use the Java XSLT-processor, linux'es xsltproc or any other XSLT processor; every XSLT processor supports at least XSLT-1.0):
<?xml version="1.0" encoding="UTF-8" ?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
<xsl:output method="xml" omit-xml-declaration="yes" encoding="UTF-8" indent="yes" />
<xsl:strip-space elements="*" />
<!-- identity template - matches everything except the things matched by other templates -->
<xsl:template match="#*|node()">
<xsl:copy>
<xsl:apply-templates select="#*|node()"/>
</xsl:copy>
</xsl:template>
<!-- Removes the elements you do not want -->
<xsl:template match="A[B[normalize-space(.)='Test Name']]" />
</xsl:stylesheet>
The output of your sample case (with a hypothetical root element) would be
<Test/>
<Test/>
<Test/>
Trying to use RegEx would be error-prone and no good-practice at all.
Why would you make it complicated if it could be so easy?
I'm trying to find an Xpath from exchange body and add a value to the tag.
<root>
<details1>
<Name>Ying</Name>
<status></status>
</details1>
<details2>
<Name>Ying</Name>
<status></status>
</details2>
</root>
I want to find xPath=root/details2/status From this exchange body and add value to the status. Since there are two occurances of status in the body String I will not be able to use String1.replace('<status></status>',<status>no</status>) Is there any way to use camel Xpath to find the correct tag and add the value?
With xpath you can find and read the correct tag. But how are you going to write it?
One solution could be to use the xpath inside an xlst tranformation.
So you can add the value you want to put in the element in a camel header "myHeader" and then use it in the xslt as an xsl:param .
Assuming the xml is in the inbox folder then
from("file:inbox?noop=true")
.setHeader("myHeader",constant("no"))
.to("xslt:mytransform.xslt")
.to("file:outbox/?fileName=out.xml");
Will put the value "no" in root/details2/status.
Where mytransform.xlst inside your /src/main/resources folder is like
<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:param name="myHeader"/>
<xsl:template match="node()|#*">
<xsl:copy>
<xsl:apply-templates select="node()|#*"/>
</xsl:copy>
</xsl:template>
<xsl:template match="/root/details2/status[. = '']">
<status><xsl:value-of select="$myHeader"/></status>
</xsl:template>
</xsl:stylesheet>
Xpath /root/details2/status[. = ''] matches empty status elements
In my xslt I'd like to look up an xml file. I need to pass the path to this file from java code.I have the followings:
...
Transformer transformer = TRANSFORMER_FACTORY.newTransformer();
transformer.setParameter("mypath", "/home/user/repository");
xslt:
<?xml version="1.0"?>
<xsl:transform xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
<xsl:output method="xml" indent="yes"/>
<xsl:param name="mypath"/>
...
<xsl:template match="connection[#id]">
<xsl:variable name="lookupStore" select="document('$mypath/myfile.xml')/connections"/>
<xsl:copy>
<xsl:apply-templates select="$lookupStore">
<xsl:with-param name="current" select="."/>
</xsl:apply-templates>
</xsl:copy>
</xsl:template>
...
<xsl:transform>
The problem is that I want to pass an absolute "base" path to the xsl, which I want to combine with the actual xml file name (myfile.xml). It seems to me that document considers file parameters relative to the location of the xsl.
Furthermore I remarked that the parameter is not picked up from the java code. I use JABX with the default Xalan XSLT processor (1.0)
I tried many variations of passing the parameters based on the other SO posts, but no success.
You need to construct a string then with the complete file URL: document(concat('file://', $mypath, '/myfile.xml')).
I am trying to replace namespace string using xslt.
I have the source namespace string and target namespace string in another xml file in the format of "namespace source="xxx" target="xxx"". All source namespace strings in my to-be-transformed xml should be changed to the corresponding target value. Only elements need to be considered as attributes don't have namespace.
I'm using the JDK default xalan xslt processor, which supports xslt 1.0. I'd like to stick to xslt 1.0 if possible, but I can change processor and use xslt 2.0 if really needed.
For example, to-be-transformed input xml:
<root xmlns="http://ns1" xmlns:ns2="http://ns2-old" xmlns:ns3="http://ns3">
<ElementA xmlns="http://ns4-old">
<ElementB/>
<ns2:elementD/>
</ElementA>
</root>
The output xml should be ("http://ns2-old" is changed to "http://ns2-new", and "http://ns4-old" is changed to "http://ns4-new"):
<root xmlns="http://ns1" xmlns:ns2="http://ns2-new" xmlns:ns3="http://ns3">
<ElementA xmlns="http://ns4-new">
<ElementB/>
<ns2:elementD/>
</ElementA>
</root>
Here is my xsl that is not working:
<?xml version="1.0" encoding="UTF-8" ?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:variable name="nsmapdoc" select="document('my-map-file')"/>
<xsl:key name="nsmap" match="//namespace/#target" use="#source"/>
<xsl:template match="node()|#*">
<xsl:copy>
<xsl:apply-templates select="node()|#*"/>
</xsl:copy>
</xsl:template>
<!-- process each element -->
<xsl:template match="element()">
<xsl:for-each select="namespace::*">
<!-- for each namespace node of the element, save the string value-->
<xsl:variable name="sourceuri"><xsl:value-of select="."/>
</xsl:variable>
<!-- get the target value for this namespace-->
<xsl:variable name="targeturi">
<xsl:for-each select="$nsmapdoc">
<xsl:value-of select="key('nsmap', $sourceuri)"/>
</xsl:for-each>
</xsl:variable>
<!-- if target value exists, replace the current namespace node string value with the target value-->
<xsl:if test="$targeturi">
<xsl:value-of select="$targeturi"/>
</xsl:if>
</xsl:for-each>
</xsl:template>
</xsl:stylesheet>
I have a few questions:
For the identity template that do the copy, why doing "match="node()|#*" instead of just "match="node()"? Is attribute also a node?
I am not sure if I did correctly to get the namespace string value (like "http://ns1", "http://ns2-old") for the element.
I think I got the key correctly. However, I am not sure if I used the type correctly---looks like targeturi variable is not a string. If key does not have the entry, what will lookup the entry return? In my case, I should replace the namespace value only for the namespace that has an entry in the map.
How to write a new string value for the namespace node?
I need to process each namespace nodes for the element. Is it the right way to define a variable inside ?
please help to see what is wrong with my xsl. Any suggestion is appreciated.
I think you have two, possibly three, separate questions here.
The first question is: how to move elements from one namespace to another, using a "map" of source-to-target namespaces. Let me answer this question first. Given:
XML
<root xmlns="http://ns1" xmlns:ns2="http://ns2-old" xmlns:ns3="http://ns3">
<ElementA xmlns="http://ns4-old">
<ElementB/>
<ns2:ElementD/>
</ElementA>
</root>
map.xml
<root>
<namespace source="http://ns2-old" target="http://ns2-new"/>
<namespace source="http://ns4-old" target="http://ns4-new"/>
</root>
The following stylesheet:
XSLT 1.0
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" version="1.0" encoding="utf-8" indent="yes"/>
<xsl:param name="nsmapdoc" select="document('map.xml')"/>
<xsl:template match="*">
<xsl:variable name="old-ns" select="namespace-uri()"/>
<xsl:variable name="map-entry" select="$nsmapdoc/root/namespace[#source=$old-ns]"/>
<xsl:variable name="new-ns">
<xsl:choose>
<xsl:when test="$map-entry">
<xsl:value-of select="$map-entry/#target"/>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="$old-ns"/>
</xsl:otherwise>
</xsl:choose>
</xsl:variable>
<xsl:element name="{local-name()}" namespace="{$new-ns}">
<xsl:copy-of select="#*"/>
<xsl:apply-templates/>
</xsl:element>
</xsl:template>
</xsl:stylesheet>
will return:
<?xml version="1.0" encoding="utf-8"?>
<root xmlns="http://ns1">
<ElementA xmlns="http://ns4-new">
<ElementB/>
<ElementD xmlns="http://ns2-new"/>
</ElementA>
</root>
Note:
ElementA and its child ElementB have been moved from namespace URI "http://ns4-old" to URI "http://ns4-new";
ElementD has been moved from namespace URI "http://ns2-old" to URI "http://ns2-new";
The prefix of ElementD has been stripped off; this is a meaningless, cosmetic change, and it should not present any problems for the receiving application;
The root element has remained in its original namespace;
Unused namespace declarations have been discarded.
Note also that we are not using a key to lookup the new namespace: using a key across documents is quite awkward in XSLT 1.0 and I have chosen to do without.
The second question is how to copy the unused namespace declarations. There are several possible answers to choose from:
It's not necessary to copy them, since that are not used for anything;
It's not possible to copy them;
It is possible to copy them by copying some element from the source document; however copying an element also copies its namespace - so this can be done only if there is a known element that is supposed to stay in its original namespace. For example, if you know beforehand that the root element is not supposed to be moved to another namespace, you can add another template to the stylesheet:
to get this result:
<?xml version="1.0" encoding="utf-8"?>
<root xmlns="http://ns1" xmlns:ns2="http://ns2-old" xmlns:ns3="http://ns3">
<ElementA xmlns="http://ns4-new">
<ElementB/>
<ElementD xmlns="http://ns2-new"/>
</ElementA>
</root>
I have an XML file that looks like
<?xml version='1.0' encoding='UTF-8'?>
<root>
<node name="foo1" value="bar1" />
<node name="foo2" value="bar2" />
</root>
I have a method
String processBar(String bar)
and I want to end up with
<?xml version='1.0' encoding='UTF-8'?>
<root>
<node name="foo1" value="processBar("bar1")" />
<node name="foo2" value="processBar("bar2")" />
</root>
Is there an easy way to do this? Preferably in Java. Note that the file is too large to safely load completely into memory. The data in the XML roughly arbitrary and processBar may be complex, so I don't want to use regular expressions.
Assuming you mean replacing the attribute values with the result of calling processBar on said attribute values...
Use the JDK's XSLT API to run the following:
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:java="http://xml.apache.org/xalan/java"
extension-element-prefixes="java">
<xsl:template match="/root/node/#value">
<xsl:attribute name="value">
<xsl:value-of select="java:com.example.yourclass.processBar(string(.))"/>
</xsl:attribute>
</xsl:template>
</xsl:stylesheet>
This uses the Xalan-Java extensions and assumes a static method. You can get an instance of an object and store it in an xsl:variable, like this:
<xsl:variable name="frobber" select="java:com.example.Frobber.new()"/>
<xsl:value-of select="java:processBar($frobber, string(.))"/>
Or somesuch.
This only works with Xalan, but since that's the XSLT processor distributed with the JDK, I doubt it will be onerous to use Xalan.
you can either parse the whole thing in a java xml parser OR just get the file content into a string and then do a regexp replace on it (using i.e. http://java.sun.com/j2se/1.5.0/docs/api/java/lang/String.html#replaceAll%28java.lang.String,%20java.lang.String%29)