Xpath with Default Namespace - java

I am trying to get all values of a certain XML element. However, the namespace is not defined. I've tried using the local-name() function but am not having any luck.
<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<entry xml:base="https://www.website.com" xmlns:d="http://schemas.microsoft.com/ado/2007/08/dataservices" xmlns:m="http://schemas.microsoft.com/ado/2007/08/dataservices/metadata" xmlns="http://www.w3.org/2005/Atom">
<id>A</id>
<title type="text"></title>
<updated>2015-07-21T02:40:30Z</updated>
<author>
<name />
</author>
<link rel="edit" title="Application" href="A(1347)" />
<link rel="http://schemas.microsoft.com/ado/2007/08/dataservices/related/b" type="application/atom+xml;type=feed" title="B" href="A(1347)/B" />
<link rel="http://schemas.microsoft.com/ado/2007/08/dataservices/related/C" type="application/atom+xml;type=feed" title="C" href="A(1347)/C" />
<link rel="http://schemas.microsoft.com/ado/2007/08/dataservices/related/D" type="application/atom+xml;type=entry" title="D" href="A(1347)/D" />
<link rel="http://schemas.microsoft.com/ado/2007/08/dataservices/related/E" type="application/atom+xml;type=feed" title="E" href="A(1347)/E">
<m:inline>
<feed>
<title type="text">E</title>
<id>1347</id>
<updated>2015-07-21T02:40:30Z</updated>
<link rel="self" title="E" href="A(1347)/E" />
<entry>
<id>www.website.com/</id>
<title type="text"></title>
<updated>2015-07-21T02:40:30Z</updated>
<author>
<name />
</author>
<link rel="edit" title="E" href="E(4294)" />
<link rel="http://schemas.microsoft.com/ado/2007/08/dataservices/related/A" type="application/atom+xml;type=entry" title="Application" href="E(4294)/A" />
<link rel="http://schemas.microsoft.com/ado/2007/08/dataservices/related/D" type="application/atom+xml;type=entry" title="D" href="E(4294)/D" />
<category term="APIModel.FileBaseDocuments" scheme="http://schemas.microsoft.com/ado/2007/08/dataservices/scheme" />
<content type="application/xml">
<m:properties>
<d:ID m:type="Edm.Int32">4294</d:ID>
<d:Type>123</d:Type>
</m:properties>
</content>
</entry>
<entry>
<id>www.website.com</id>
<title type="text"></title>
<updated>2015-07-21T02:40:30Z</updated>
<author>
<name />
</author>
<link rel="edit" title="E" href="E(4295)" />
<link rel="http://schemas.microsoft.com/ado/2007/08/dataservices/related/A" type="application/atom+xml;type=entry" title="A" href="E(4295)/A" />
<link rel="http://schemas.microsoft.com/ado/2007/08/dataservices/related/D" type="application/atom+xml;type=entry" title="D" href="E(4295)/D" />
<category term="APIModel.FileBaseDocuments" scheme="http://schemas.microsoft.com/ado/2007/08/dataservices/scheme" />
<content type="application/xml">
<m:properties>
<d:ID m:type="Edm.Int32">4295</d:ID>
<d:Type>456</d:Type>
</m:properties>
</content>
</entry>
</feed>
</m:inline>
</link>
</entry>
I want to retrieve all values inside of "m:properties/d:ID m:type="Edm.Int32" (in this case 4294) but I am getting no luck. So for one file, there would be one "feed" tag filled with multiple "entry" tags. Inside these tags there would be one "m:properties/d:ID m:type="Edm.Int32" which I need to retrieve. Any suggestion on what the correct xPath would be for this situation?

Instead of resorting to namespace agnostic xml by using local-name(), why not register a namespace for the default namespace, e.g. x prefix for xmlns:x="http://www.w3.org/2005/Atom", and then your Xpath would be something like:
//x:feed/x:entry/x:content/m:properties/d:ID[#m:type='Edm.Int32']
The local-name() approach is more verbose:
//*[local-name()='feed']/*[local-name()='entry']/*[local-name()='content']
/*[local-name()='properties']/*[local-name()='ID' and #m:type='Edm.Int32']
Example of both approaches here

Use the #exclude-result-prefixes attribute to filter out the extraneous namespaces in your output. You don't need to declare the http://www.w3.org/2005/Atom in your transform, unless you need it for other purposes, but even so, it is probably a good thing.
Just match on expression m:properties/d:ID[#m:type='Edm.Int32'] to get your required data.
For example, this transform, when applied to your given input document ....
<xsl:stylesheet
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:d="http://schemas.microsoft.com/ado/2007/08/dataservices"
xmlns:m="http://schemas.microsoft.com/ado/2007/08/dataservices/metadata"
xmlns:atom="http://www.w3.org/2005/Atom"
version="2.0"
exclude-result-prefixes="xsl d m atom">
<xsl:output encoding="utf-8" omit-xml-declaration="yes" indent="yes" />
<xsl:template match="/">
<Edm.Int32s>
<xsl:apply-templates />
</Edm.Int32s>
</xsl:template>
<xsl:template match="*">
<xsl:apply-templates />
</xsl:template>
<xsl:template match="text()" />
<xsl:template match="m:properties/d:ID[#m:type='Edm.Int32']">
<value><xsl:value-of select="text()" /></value>
</xsl:template>
</xsl:stylesheet>
... yields output ....
<edm.int32s>
<value>4294</value>
<value>4295</value>
</edm.int32s>
Note
I have assumed from your question tags that you want an XSLT transform. If you just wanted a simple XPath expression to apply directly to the document, you could use ...
//m:properties/d:ID[#m:type='Edm.Int32']/text()
(passing, of course, the namespace declarations for m and d).

Related

Sort nodes in XML using DOM parser

How can I sort the XML nodes according to the tag and append in
the new XML using DOM parser or can it be done using DOM parser. We've
used DOM parser extensively for appending nodes into a new file but I am not able to sort the nodes.
Any help would be highly appreciated.
Input.xml
<rss version="2.0">
<Configs>
<Value>defaultValue</Value>
<Config name="test1">
<title>Title 1</title>
<author>Author1</author>
<value>5600</value>
<order>02</order>
</Config>
<Config name="test2">
<title>Title 2</title>
<author>Author2</author>
<Value>6100</Value>
<order>01</order>
</Config>
</Configs>
<Ratings>
<body>
<Items name="ac_object1">
<something1>something1</something1>
<value>someValue1</value>
<order>02</order>
</Items>
<Items name="op_object2">
<something1>something2</something1>
<value>someValue2</value>
<order>03</order>
</Items>
<Items name="vt_object3">
<something1>something3</something1>
<value>someValue3</value>
<order>01</order>
</Items>
</body>
</Ratings>
</rss>
Expected Output.xml
<rss version="2.0">
<Configs>
<Value>defaultValue</Value>
<Config name="test2">
<title>Title 2</title>
<author>Author2</author>
<Value>6100</Value>
<order>01</order>
</Config>
<Config name="test1">
<title>Title 1</title>
<author>Author1</author>
<value>5600</value>
<order>02</order>
</Config>
</Configs>
<Ratings>
<body>
<Items name="vt_object3">
<something1>something3</something1>
<value>someValue3</value>
<order>01</order>
</Items>
<Items name="ac_object1">
<something1>something1</something1>
<value>someValue1</value>
<order>02</order>
</Items>
<Items name="op_object2">
<something1>something2</something1>
<value>someValue2</value>
<order>03</order>
</Items>
</body>
</Ratings>
</rss>
You really don't want to do this using low-level DOM interfaces. Here's how to do it in XSLT 3.0 (which you can call from Java after installing Saxon-HE):
<xsl:transform xmlns:xsl="http://www.w3.org/1999/XSL/Transform> version="3.0">
<xsl:mode on-no-match="shallow-copy"/>
<xsl:strip-space elements="*"/>
<xsl:output method="xml" indent="yes"/>
<xsl:template match="*[*/order]">
<xsl:copy>
<xsl:apply-templates>
<xsl:sort select="number(order)"/>
</xsl:apply-templates>
</xsl:copy>
</xsl:template>
</xsl:transform>
With a few extra lines of code you could also do it using XSLT 1.0, which comes bundled with the JDK.
How it works:
The xsl:mode declaration says that the default action for elements is to copy the element and then process its children
xsl:strip-space says ignore whitespace in the input
xsl:output says add indentation in the output
The xsl:template rule says that when processing an element that has order elements among its grandchildren, copy the start and end tag, and process the children in sorted order of the numeric value of their order child element.

How to get xml attribute value containing multiple colons in a single tag i.e. namespaces?

Here is my XML:
<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<feed xml:base="XYZ" xmlns:d="http://schemas/services" xmlns:m="http://schemas/metadata" xmlns="http://www.abc.pqr/Atom">
<title type="text">Title</title>
<id>Id</id>
<updated>2018-02-08</updated>
<link rel="self" title="titl" href="title" />
<entry m:etag="W/"25"">
<id>id1</id>
<title type="text">title_text</title>
<updated>2018-02-08</updated>
<author>
<name />
</author>
<link m:etag=""{acx},25"" rel="edit-media" title="title_value" />
<link rel="edit" title="title" />
<link rel="http://az.com/CreatedBy" type="application/atom+xml;type=entry" title="CreatedBy" href="title/CreatedBy" />
<link rel="http://az.com/ModifiedBy" type="application/atom+xml;type=entry" title="ModifiedBy" href="title/ModifiedBy" />
<link rel="http://az.com/CheckedOutTo" type="application/atom+xml;type=entry" title="CheckedOutTo" href="title/CheckedOutTo" />
<category term="xyz" scheme="xyz" />
<content type="application/octetstream" src="http://collb.xlsm" />
<m:properties xmlns:m="http://xyz/metadata" xmlns:d="wsx/dataservices">
<d:ContentTypeID>contentId</d:ContentTypeID>
<d:Dept>21</d:Dept>
<d:Class>0</d:Class>
<d:Id m:type="Edm.Int32">2121</d:Id>
</m:properties>
</entry>
<link rel="next" href="http://tyu.com" />
</feed>
I want to read values corresponding to <d:Id m:type> and <entry m:etag> i.e. output should be 2121 and 25.
How should I read these.
P.S: I have gone through similar questions asked on stackoverflow, but nothing worked for me.
Part of my code:
DocumentBuilderFactory builderFactory = DocumentBuilderFactory.newInstance();
builderFactory.setNamespaceAware(true);
DocumentBuilder documentBuilder = documentBuilder = builderFactory.newDocumentBuilder();
Document doc = documentBuilder.parse(connection.getInputStream());
Element element = doc.getDocumentElement();
element.getAttributeNodeNS("http://schemas/metadata", "type");
You haven't said which of the vast choice of Java APIs you are using. I guess that probably means you're using DOM. I don't know why anyone still uses DOM when there are much better alternatives available, but they do.
Whichever API you are using, you'll find that there's probably a method something like
element.getAttributeValue(namespace, localname)
To read the m:type attribute, you would use
element.getAttributeValue("http://schemas/metadata", "type")

How to modify the attribute values in XML file using Java

I need to modifies the XML, I have a parent tag parts which contain child tag part and Child tag sub child tags, They are Item,
brand, Manufacturer, Model, Cost. And it have a Brands tag and it contain Tag name called Brand. I need to change the brand attribute
(identifier) value. If the Brand child tag Contain (textcontent) text (Geforce_GT) is equal to the text in part tag : brand child tag : text
(Geforce_GT). Then Id attribute value of brand tag in part tag should assign to the Identifeir attribute in Brand tag child of Brands Parent tag.
<?xml version="1.0" ?>
<!DOCTYPE PARTS SYSTEM "parts.dtd">
<?xml-stylesheet type="text/css" href="xmlpartsstyle.css" ?>
<PARTS>
<TITLE>Computer Parts</TITLE>
<PART>
<ITEM id="CP1809_E1">Motherboard</ITEM>
<MANUFACTURER>ASUS</MANUFACTURER>
<MODEL>P3B-F</MODEL>
<COST>123.00</COST>
</PART>
<PART>
<ITEM id="CP1809_E2">Video Card</ITEM>
<BRAND id="CP1809_B1">Geforce_GT</BRAND>
<MANUFACTURER>ATI</MANUFACTURER>
<MODEL>All-in-Wonder Pro</MODEL>
<BRAND id="CP1809_B2">730_64-BIT</BRAND>
<COST>160.00</COST>
</PART>
<PART>
<ITEM id="CP1809_E3">Sound Card</ITEM>
<MANUFACTURER>Creative Labs</MANUFACTURER>
<MODEL>Sound Blaster Live</MODEL>
<COST>80.00</COST>
</PART>
<PART>
<ITEM id="CP1809_E3">inch Monitor</ITEM>
<MANUFACTURER>LG Electronics</MANUFACTURER>
<MODEL>995E</MODEL>
<COST>290.00</COST>
</PART>
<BRANDS>
<BRAND identifier="CP1809_E2">
<TEXTCONTENT>Geforce_GT</TEXTCONTENT>
</BRAND>
<BRAND identifier="CP1809_E2">
<TEXTOVERVIEW>730_64-BIT</TEXTOVERVIEW>
</BRAND>
<BRAND identifier="B1809_E3">
<TEXT>Empty</TEXT>
</BRAND>
<BRAND identifier="B1809_E4">
<TEXT>Empty</TEXT>
</BRAND>
</BRANDS>
</PARTS>
I need to make modifications such as:
<BRANDS>
<BRAND identifier = "CP1809_B1">
<TEXTCONTENT>Geforce_GT<TEXTCONTENT>
</BRAND>
<BRAND identifier = "CP1809_B2">
<TEXTCONTENT>730_64-BIT<TEXTCONTENT>
</BRAND>
<!-- ... -->
<BRANDS>
XSLT can do that (see example at http://xsltransform.net/ej9EGda):
<xsl:transform xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
<xsl:key name="desc" match="PART/BRAND" use="."/>
<xsl:template match="#*|node()">
<xsl:copy>
<xsl:apply-templates select="#*|node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="BRANDS/BRAND[key('desc', TEXTCONTENT)]/#identifier">
<xsl:attribute name="{name()}">
<xsl:value-of select="key('desc', ../TEXTCONTENT)/#id"/>
</xsl:attribute>
</xsl:template>
</xsl:transform>
On the Java platform you have a choice of XSLT processors like Saxon 9 for XSLT 2.0 or Xalan or Saxon 6 for XSLT 1.0 and the built-in version of Xalan in the JRE.

Showing results from two different XML files in Java

So I am trying to pull results from two different XML files using XSLT in order to show a Restaurant Review. I have Restaurant details in allRestaurants.xml and have all of the reviews for these restaurants in allReviews.xml. I have currently stored a tag against each restaurant, and the reviews are each associated with a specific restaurant as well, so carry the same tag. I need to build a page that takes the restaurant with ID 1 and beneath is show the reviews for that restaurant. The reviews are stored with the exactly the same 1 as per below. Please help.
allRestaurants.xml
<restaurants>
<restaurant>
<restaurant_id>1</restaurant_id>
<name>The Jackaroo</name>
<street_address>107-109 Darlinghurst Road</street_address>
<postcode>2011</postcode>
<city>Sydney</city>
<state>NSW</state>
<country>Australia</country>
<email>info#jackaroo.com.au</email>
<telephone>93322244</telephone>
<stars>3</stars>
</restaurant>
<restaurant>
<restaurant_id>2</restaurant_id>
<name>Four Seasons restaurant Sydney</name>
<street_address>199 George Street</street_address>
<postcode>2000</postcode>
<city>Sydney</city>
<state>NSW</state>
<country>Australia</country>
<email>info#sydneyfourseasons.com.au</email>
<telephone>92503100</telephone>
<stars>5</stars>
</restaurant>
</restaurants>
allReviews.xml
<reviews>
<review id="1">
<restaurant_id>1</restaurant_id>
<author_id>1</author_id>
<headline>Clean Bare-Bones Hostel</headline>
<details>
Example text here
</details>
<rating>3</rating>
<date>1388782853</date>
</review>
<review id="2">
<restaurant_id>1</restaurant_id>
<author_id>3</author_id>
<headline>Wouldn't Recommend</headline>
<details>
Example text here
</details>
<rating>2</rating>
<date>1368748800</date>
</review>
<review id="3">
<restaurant_id>2</restaurant_id>
<author_id>2</author_id>
<headline>Overall I Enjoyed</headline>
<details>
Example text here
</details>
<rating>4</rating>
<date>1378788850</date>
</review>
</reviews>
I thought maybe merging them into one XML file like so would do the trick, but even then, I'm not sure where to start:
oneHotel.xml
<?xml-stylesheet type="text/xsl" href="oneHotel.xsl"?>
<list>
<entry name="allHotels.xml" />
<entry name="reviews.xml" />
</list>
This is as far as I got in the XSLT doc, and am drawing a massive blank. I don't even know where to start:
oneHotel.xsl
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="html"/>
<xsl:copy-of name="restaurant" select="document('allRestaurants.xml')
/restaurants/restaurant[restaurant_id=1]"/>
<xsl:copy-of name="reviews" select="document('allReviews.xml')
/reviews/review[restaurant_id=1]"/>
<xsl:template match="/">
<xsl:choose>
<xsl:when test="document('allRestaurants.xml')
/restaurants/restaurant[restaurant_id=1]"/>
<h2><xsl:value-of select="name"/></h2>
</xsl:choose>
<h2><xsl:value-of select="$restaurant/name"/></h2>
</xsl:template>
</xsl:stylesheet>
Try this as your starting point:
XSLT 1.0
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="html" encoding="UTF-8"/>
<xsl:param name="path-to-reviews" select="'allReviews.xml'"/>
<xsl:key name="review-by-restaurant-id" match="review" use="restaurant_id" />
<xsl:template match="/restaurants">
<html>
<body>
<h1>Restaurant Reviews</h1>
<xsl:apply-templates select="restaurant"/>
</body>
</html>
</xsl:template>
<xsl:template match="restaurant">
<h2>
<xsl:value-of select="name"/>
</h2>
<xsl:variable name="id" select="restaurant_id" />
<!-- switch context to lookup document in order to use key -->
<xsl:for-each select="document($path-to-reviews)">
<xsl:for-each select="key('review-by-restaurant-id', $id)">
<h3>
<xsl:value-of select="headline"/>
</h3>
<p>
<xsl:value-of select="details"/>
</p>
</xsl:for-each>
</xsl:for-each>
</xsl:template>
</xsl:stylesheet>
This assumes that you are instructing your XSLT processor to process the allRestaurants.xml document and passing the path to the allReviews.xml document as a parameter.
You didn't tell us what you want your final result to look like, so I just made up a very basic page.

Populate XML template-file from XPath Expressions?

What would be the best way to populate (or generate) an XML template-file from a mapping of XPath expressions?
The requirements are that we will need to start with a template (since this might contain information not otherwise captured in the XPath expressions).
For example, a starting template might be:
<s11:Envelope xmlns:s11='http://schemas.xmlsoap.org/soap/envelope/'>
<ns1:create xmlns:ns1='http://predic8.com/wsdl/material/ArticleService/1/'>
<article xmlns:ns1='http://predic8.com/material/1/'>
<name>?XXX?</name>
<description>?XXX?</description>
<price xmlns:ns1='http://predic8.com/common/1/'>
<amount>?999.99?</amount>
<currency xmlns:ns1='http://predic8.com/common/1/'>???</currency>
</price>
<id xmlns:ns1='http://predic8.com/material/1/'>???</id>
</article>
</ns1:create>
</s11:Body>
</s11:Envelope>
Then we are supplied, something like:
expression: /create/article[1]/id => 1
expression: /create/article[1]/description => bar
expression: /create/article[1]/name[1] => foo
expression: /create/article[1]/price[1]/amount => 00.00
expression: /create/article[1]/price[1]/currency => USD
expression: /create/article[2]/id => 2
expression: /create/article[2]/description => some name
expression: /create/article[2]/name[1] => some description
expression: /create/article[2]/price[1]/amount => 00.01
expression: /create/article[2]/price[1]/currency => USD
We should then generate:
<ns1:create xmlns:ns1='http://predic8.com/wsdl/material/ArticleService/1/'>
<article xmlns:ns1='http://predic8.com/material/1/'>
<name xmlns:ns1='http://predic8.com/material/1/'>foo</name>
<description>bar</description>
<price xmlns:ns1='http://predic8.com/common/1/'>
<amount>00.00</amount>
<currency xmlns:ns1='http://predic8.com/common/1/'>USD</currency>
</price>
<id xmlns:ns1='http://predic8.com/material/1/'>1</id>
</article>
<article xmlns:ns1='http://predic8.com/material/2/'>
<name>some name</name>
<description>some description</description>
<price xmlns:ns1='http://predic8.com/common/2/'>
<amount>00.01</amount>
<currency xmlns:ns1='http://predic8.com/common/2/'>USD</currency>
</price>
<id xmlns:ns1='http://predic8.com/material/2/'>2</id>
</article>
</ns1:create>
I am implemented in Java, although I would prefer an XSLT-based solution if one is possible.
PS: This question is the reverse of another question I recently asked.
This transformation creates from the "expressions" an XML document that has the structure of the wanted result -- it remains to transform this result into the final result:
<xsl:stylesheet version="2.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
xmlns:my="my:my">
<xsl:output omit-xml-declaration="yes" indent="yes"/>
<xsl:variable name="vPop" as="element()*">
<item path="/create/article[1]/id">1</item>
<item path="/create/article[1]/description">bar</item>
<item path="/create/article[1]/name[1]">foo</item>
<item path="/create/article[1]/price[1]/amount">00.00</item>
<item path="/create/article[1]/price[1]/currency">USD</item>
<item path="/create/article[1]/price[2]/amount">11.11</item>
<item path="/create/article[1]/price[2]/currency">AUD</item>
<item path="/create/article[2]/id">2</item>
<item path="/create/article[2]/description">some name</item>
<item path="/create/article[2]/name[1]">some description</item>
<item path="/create/article[2]/price[1]/amount">00.01</item>
<item path="/create/article[2]/price[1]/currency">USD</item>
</xsl:variable>
<xsl:template match="/">
<xsl:sequence select="my:subTree($vPop/#path/concat(.,'/',string(..)))"/>
</xsl:template>
<xsl:function name="my:subTree" as="node()*">
<xsl:param name="pPaths" as="xs:string*"/>
<xsl:for-each-group select="$pPaths"
group-adjacent=
"substring-before(substring-after(concat(., '/'), '/'), '/')">
<xsl:if test="current-grouping-key()">
<xsl:choose>
<xsl:when test=
"substring-after(current-group()[1], current-grouping-key())">
<xsl:element name=
"{substring-before(concat(current-grouping-key(), '['), '[')}">
<xsl:sequence select=
"my:subTree(for $s in current-group()
return
concat('/',substring-after(substring($s, 2),'/'))
)
"/>
</xsl:element>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="current-grouping-key()"/>
</xsl:otherwise>
</xsl:choose>
</xsl:if>
</xsl:for-each-group>
</xsl:function>
</xsl:stylesheet>
When this transformation is applied on any XML document (not used), the result is:
<create>
<article>
<id>1</id>
<description>bar</description>
<name>foo</name>
<price>
<amount>00.00</amount>
<currency>USD</currency>
</price>
<price>
<amount>11.11</amount>
<currency>AUD</currency>
</price>
</article>
<article>
<id>2</id>
<description>some name</description>
<name>some description</name>
<price>
<amount>00.01</amount>
<currency>USD</currency>
</price>
</article>
</create>
Note:
You need to transform the "expressions" you are given into the format used in this transformation -- this is easy and straightforward.
In the final transformation you need to copy every node "as-is" (using the identity rule), with the exception that the top node should be generated in the "http://predic8.com/wsdl/material/ArticleService/1/" namespace. Note that the other namespaces present in the "template" are not used and can be safely ommitted.
This solution requires you to re-organise your XPATH input information slightly, and to allow a 2-step transformation. The first transformation will write the stylesheet, which will be executed in the second transformation - Thus the client is required to do two invocations of the XSLT engine. Let us know if this is a problem.
Step One
Please re-organise your XPATH information into an XML document like so. It should not be difficult to do, and even an XSLT script could be written to do the job.
<paths>
<rule>
<match>article[1]/id[1]</match>
<namespaces>
<namespace prefix="ns1">http://predic8.com/wsdl/material/ArticleService/1/</namespace>
<!-- The namespace node declares a namespace that is used in the match expression.
There can be many of these. It is not required to define the s11: namespace,
nor the ns1 namespace. -->
</namespaces>
<replacement>1</replacement>
</rule>
<rule>
<match>article[1]/description[1]</match>
<namespaces/>
<replacement>bar</replacement>
</rule>
... etc ...
</paths>
Solution constraints
In the above rules document we are constrained so that:
The match is implicitly prefixed 'expression: /create/'. Don't put that explicitly.
All matches must begin like article[n] where n is some ordinal number.
We can't have zero rules.
Any prefixes that you use in the match, other than s11="http://schemas.xmlsoap.org/soap/envelope/" and ns1="http://predic8.com/wsdl/material/ArticleService/1/". (Note: I don't think it is valid for namespaces to end in '/' - but not sure about that), are defined in the namespaces node.
The above is the input document to the step one transformation. Apply this document to this style-sheet ...
<xsl:stylesheet version="2.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:step2="http://www.w3.org/1999/XSL/Transform-step2"
xmlns:s11="http://schemas.xmlsoap.org/soap/envelope/"
xmlns:ns1="http://predic8.com/wsdl/material/ArticleService/1/"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
exclude-result-prefixes='xsl'>
<xsl:output method="xml" indent="yes" encoding="UTF-8" />
<xsl:namespace-alias stylesheet-prefix="step2" result-prefix="xsl"/>
<xsl:template match="/">
<step2:stylesheet version="2.0">
<step2:output method="xml" indent="yes" encoding="UTF-8" />
<step2:variable name="replicated-template" as="element()*">
<step2:apply-templates select="/" mode="replication" />
</step2:variable>
<step2:template match="#*|node()" mode="replication">
<step2:copy>
<step2:apply-templates select="#*|node()" mode="replication" />
</step2:copy>
</step2:template>
<step2:template match="/s11:Envelope/s11:Body/ns1:create/article" mode="replication">
<step2:variable name="replicant" select="." />
<step2:for-each select="for $i in 1 to
{max(for $m in /paths/rule/match return
xs:integer(substring-before(substring-after($m,'article['),']')))}
return $i">
<step2:for-each select="$replicant">
<step2:copy>
<step2:apply-templates select="#*|node()" mode="replication" />
</step2:copy>
</step2:for-each>
</step2:for-each>
</step2:template>
<step2:template match="#*|node()">
<step2:copy>
<step2:apply-templates select="#*|node()"/>
</step2:copy>
</step2:template>
<step2:template match="/">
<step2:apply-templates select="$replicated-template" />
</step2:template>
<xsl:apply-templates select="paths/rule" />
</step2:stylesheet>
</xsl:template>
<xsl:template match="rule">
<step2:template match="s11:Envelope/s11:Body/ns1:create/{match}">
<xsl:for-each select="namespaces/namespace">
<xsl:namespace name="{#prefix}" select="." />
</xsl:for-each>
<step2:copy>
<step2:apply-templates select="#*"/>
<step2:value-of select="'{replacement}'"/>
<step2:apply-templates select="*"/>
</step2:copy>
</step2:template>
</xsl:template>
</xsl:stylesheet>
Step Two
Apply your soap envelope file, as an input document, to the style-sheet which was output from step one. The result is the original soap document, altered as required. This is a sample of a step two style-sheet, with just the first rule (/create/article[1]/id => 1) being considered for the sake of simplicity of illustration.
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:s11="http://schemas.xmlsoap.org/soap/envelope/"
version="2.0">
<xsl:output method="xml" indent="yes" encoding="UTF-8"/>
<xsl:template match="#*|node()">
<xsl:copy>
<xsl:apply-templates select="#*|node()"/>
</xsl:copy>
</xsl:template>
<xsl:template xmlns:ns1="http://predic8.com/wsdl/material/ArticleService/1/"
match="/s11:Envelope/s11:Body/ns1:create[1]/article[1]/id[1]">
<xsl:copy>
<xsl:apply-templates select="#*"/>
<xsl:value-of select="'1'"/>
<xsl:apply-templates select="*"/>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
More solution constraints
The template document must contain at least one /s11:Envelope/s11:Body/ns1:create/article . Only the article node is replicated (deeply) as required by rules. Other than than it can be any structure.
The template document cannot contain nested levels of the s11:Envelope/s11:Body/ns1:create node.
Explanation
You will notice that your XPATH expressions are not far removed from a match condition of template. Therefore it is not too difficult to write a stylesheet which re-expresses your XPATH and replacement values as template rules. When writing a style-sheet writing style-sheet the xsl:namespace-alias enables us to disambiguate "xsl:" as an instruction and "xsl:" as intended output. When XSLT 3.0 comes along, we are quiet likely to be able to reduce this algorithm into one step, as it will allow dynamic XPATH evaluation, which is really the nub of your problem. But for the moment we must be content with a 2-step process.
The second style-sheet is a two-phase transformation. The first stage replicates the template from the article level, as many times as needed by the rules. The second phase parses this replicated template, and applies the dynamic rules substituting text values as indicated by the XPATHs.
UPDATE
My original post was wrong. Thanks to Dimitre for pointing out the error. Please find updated solution above.
After-thought
If a two-step solultion is too complicated, and you are running on a wintel platform, you may consider purchasing the commercial version of Saxon. I believe that the commercial version has a dynamic XPATH evaluation function. I can't give you such a solution because I don't have the commercial version. I imagine a solution using an evaluate() function would be a lot simpler. XSLT is just a hobby for me. But if you are using XSLT for business purposes, the price is quiet reasonable.

Categories