add parentheses around output variables - xslt

Given the following code:
https://xsltfiddle.liberty-development.net/pNEj9dR
i have not managed to get as output the parentheses i need around each variable.
Iitry with:
({sbf[#cd = 'a']})
The above will output () whenever {sbf[#cd = 'a']} does not exist.
Desired output:
<?xml version="1.0" encoding="UTF-8"?>
<O-PM>
<ListRcs>
<rc>
<mdtd>
<rc>
<title xml:lang="el">(Christoph Kolumbus) (Julie Nat) / (Darius Milhaud) (Erich kleiber) ; (Franz Ludwig Horth)</title>
</rc>
</mdtd>
</rc>
<rc>
<mdtd>
<rc>
<title xml:lang="el">(Chris Prante) : ("Chris Dietz") </title>
</rc>
</mdtd>
</rc>
</ListRcs>
</O-PM>
How the above could be achieved?
if i surround the variables with parentheses,ie

The previous answer already showed you to output some characters only when your data exists by using it at the right side of the map operator !: {sbf[#cd = 'a']!('(',.,')')}. That might introduce spaces so perhaps you rather want {sbf[#cd = 'a']!('(' || . || ')')}.

Related

Xmlstarlet select String+number for update

I want to update an element in my xml-File, with some dynamic names.
The name (paraName) is always GLOBPARA followed by a number, like you can see in the following example. These are the elements I want to change the value.
The file is also filled with some elements, which are also starting with an GLOBPARA, but have no simple number following. These elements I don't want to change.
With the following command I can change every element, which has a GLOBPARA in the name. Including the unwanted ones.
xmlstarlet ed --update "//globPara[contains(paramName, 'GLOBPARA')]/paramValue" -v "100" test.xml
Question:
How do I change only the ones, containing a String (GLOBPARA) and some random Numbers?
Before:
<?xml version="1.0" encoding="UTF-8"?>
<container>
<dataList>
<globPara>
<paramName>GLOBPARA260</paramName>
<paramValue>0</paramValue>
</globPara>
<globPara>
<paramName>GLOBPARAMON_BAD_TEST_18_1_SGB_IV</paramName>
<paramValue>2555</paramValue>
</globPara>
</dataList>
</container>
Wanted result:
<?xml version="1.0" encoding="UTF-8"?>
<container>
<dataList>
<globPara>
<paramName>GLOBPARA260</paramName>
<paramValue>100</paramValue>
</globPara>
<globPara>
<paramName>GLOBPARAMON_BAD_TEST_18_1_SGB_IV</paramName>
<paramValue>2555</paramValue>
</globPara>
</dataList>
</container>
I tried it with the regex d+, but it didn't work.
xmlstarlet ed --update "//globPara[contains(paramName, 'GLOBPARA[\d+]')]/paramValue" -v "100" test.xml
You can try below XPath
//globPara[number(substring-after(paramName, 'GLOBPARA'))>=0]/paramValue
This will return you paramValue of globPara node that contains paraName child with text in format GLOBPARAXXX where XXX is ANY positive number

Freemarker - XSLT equivalent of copy-of

I'm trying to simulate the copy-of function in XSLT where I want everything within a node outputted in the response.
Using this template
<#ftl ns_prefixes={"D": "http://milyn.codehaus.org/Smooks"} output_format="XML">
${Order.orderitem.##markup}
Facing 2 issues here
The output i get transformed the <, > as well of the XML tags. I do need XML formatting to escape invalid characters like & etc.
How can i remove the namescapes that appears in every node
My response is
<orderitem xmlns="http://milyn.codehaus.org/Smooks"><position>1</position><quantity>1</quantity><productid>364</productid><title>The 40YearOld</title><price>29.98</price></orderitem><orderitem xmlns="http://milyn.codehaus.org/Smooks"><position>2</position><quantity>1</quantity><productid>299</productid><title>Pulp Fiction</title><price>29.99</price></orderitem>
Input being
<Order xmlns="http://milyn.codehaus.org/Smooks" xmlns:xsi="http://www.w3.org/2001/XMLSchemainstance">
<header>
<orderid>1</orderid>
<statuscode>0</statuscode>
<netamount>59.97</netamount>
<totalamount>64.92</totalamount>
<tax>4.95</tax>
<date>Wed Nov 15 13:45:28 EST 2006</date>
</header>
<customerdetails>
<username>user1</username>
<name>
<firstname>Harry</firstname>
<lastname>Fletcher</lastname>
</name>
<state>South Dakota</state>
</customerdetails>
<orderitem>
<position>1</position>
<quantity>1</quantity>
<productid>364</productid>
<title>The 40YearOld</title>
<price>29.98</price>
</orderitem>
<orderitem>
<position>2</position>
<quantity>1</quantity>
<productid>299</productid>
<title>Pulp Fiction</title>
<price>29.99</price>
</orderitem>
To prevent auto-escaping: ${Order.orderitem.##markup?no_esc}. (Unfortunately XML wrapping way predates auto-escaping, so it has remained like so...)
Prevent repeated xmlns-es... you can't. The problem is that the orderitem-s has no common ancestor as far as ##markup can know, where a common xmlns could solve this, so it does the safest thing.

XSLT Filter on property

I am needing a little help with filtering my xml based on a property
I have the XML in the following format:
<?xml version="1.0" encoding="utf-8" ?>
<root id="-1">
<LandingPage id="1067" parentID="1050" level="2"
writerID="0" creatorID="0" nodeType="1066" template="1073"
sortOrder="0" createDate="2013-02-04T14:29:39"
updateDate="2013-02-07T11:08:27" nodeName="About"
urlName="about" writerName="Pete" creatorName="Pete"
path="-1,1050,1067" isDoc="">
<hideInNavigation>0</hideInNavigation>
</LandingPage>
</root>
What I need to do is filter these elements where hideInNavigation = 0
I have tried the following:
[#isDoc and #hideInNavigation ='0']
(I need the #isDoc attribute too) but realised this would only work if hideInNavigation was an attribute of the LandingPage tag so I tried
value['hideInNavigation'='0']
but this didn't seem to do anything either. After much searching for the answer, I haven't come up with anything so was wondering if it is possible
Supposing the current context was the <root> element, you could select the LandingPages with hideInNavigation = 0 with:
LandingPage[hideInNavigation = '0']
If you would share your XSLT, I van give you more specific guidance on how to amend it for this particular case.
And was the #isDoc test in your first example something you wanted? Do you want to filter LandingPages that have an isDoc attribute and a hideInNavigation value of 0?
'hideInNavigation'='0' compares the two strings 'hideInNavigation' and '0', which are guaranteed to be different.
In the context of root, LandingPage[hideInNavigation=0] would match the LandingPage element in your example.
This XPath return all LandingPage with isDoc attribute empty and hideInNavigation element content is '0'
//LandingPage[#isDoc="" and hideInNavigation='0']

BaseX XQuery replace

I have the following problem. I want to replace a value of an element in my xquery-file by using baseX as the database. The xquery code is as follows:
let $db := doc('update.xml')
replace value of node $db//elem with 'haha'
return <result> {$db//elem/text()} </result>
The xml document contains the following elements:
<?xml version="1.0" encoding="ISO-8859-1"?>
<root xmlns:xs="http://www.w3.org/2001/XMLSchema-instance">
<check>
<ok>
<elem>test</elem>
<help></help>
</ok>
</check>
</root>
Everytime I want to execute this xquery an error like this is thrown:
Expecting 'where', 'order' or 'return' expression
so what should i do or change, to just replace the text "test" by "haha" in the element ?
If I use just this line of code it works, but I have to read out of URL-Parameter so I need more lines of code, except the "replace...." line!
let starts a flwor-expression which may not directly contain update statements. You will have to put a return between these two:
let $db := doc('update.xml')
return
replace value of node $db//elem with 'haha'
You will also be able to do arbitrary calculations, but make sure not to have any output returned by your query.
There is no way to use updating statements and return a result at the same time.

XSLT Select all nodes containing a specific substring

I'm trying to write an XPath that will select certain nodes that contain a specific word.
In this case the word is, "Lockwood". The correct answer is 3. Both of these paths give me 3.
count(//*[contains(./*,'Lockwood')])
count(BusinessLetter/*[contains(../*,'Lockwood')])
But when I try to output the text of each specific node
//*[contains(./*,'Lockwood')][1]
//*[contains(./*,'Lockwood')][2]
//*[contains(./*,'Lockwood')][3]
Node 1 ends up containing all the text and nodes 2 and 3 are blank.
Can some one please tell me what's happening or what I'm doing wrong.
Thanks.
<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet type="text/xsl" href="XPathFunctions.xsl"?>
<BusinessLetter>
<Head>
<SendDate>November 29, 2005</SendDate>
<Recipient>
<Name Title="Mr.">
<FirstName>Joshua</FirstName>
<LastName>Lockwood</LastName>
</Name>
<Company>Lockwood & Lockwood</Company>
<Address>
<Street>291 Broadway Ave.</Street>
<City>New York</City>
<State>NY</State>
<Zip>10007</Zip>
<Country>United States</Country>
</Address>
</Recipient>
</Head>
<Body>
<List>
<Heading>Along with this letter, I have enclosed the following items:</Heading>
<ListItem>two original, execution copies of the Webucator Master Services Agreement</ListItem>
<ListItem>two original, execution copies of the Webucator Premier Support for Developers Services Description between Lockwood & Lockwood and Webucator, Inc.</ListItem>
</List>
<Para>Please sign and return all four original, execution copies to me at your earliest convenience. Upon receipt of the executed copies, we will immediately return a fully executed, original copy of both agreements to you.</Para>
<Para>Please send all four original, execution copies to my attention as follows:
<Person>
<Name>
<FirstName>Bill</FirstName>
<LastName>Smith</LastName>
</Name>
<Address>
<Company>Webucator, Inc.</Company>
<Street>4933 Jamesville Rd.</Street>
<City>Jamesville</City>
<State>NY</State>
<Zip>13078</Zip>
<Country>USA</Country>
</Address>
</Person>
</Para>
<Para>If you have any questions, feel free to call me at <Phone>800-555-1000 x123</Phone> or e-mail me at <Email>bsmith#webucator.com</Email>.</Para>
</Body>
<Foot>
<Closing>
<Name>
<FirstName>Bill</FirstName>
<LastName>Smith</LastName>
</Name>
<JobTitle>VP of Operations</JobTitle>
</Closing>
</Foot>
</BusinessLetter>
But when I try to output the text of
each specific node
//*[contains(./*,'Lockwood')][1]
//*[contains(./*,'Lockwood')][2]
//*[contains(./*,'Lockwood')][3]
Node 1 ends up containing all the text
and nodes 2 and 3 are blank
This is a FAQ.
//SomeExpression[1]
is not the equivalent to
(//someExpression)[1]
The former selects all //SomeExpression nodes that are the first child of their parent.
The latter selects the first (in document order) of all //SomeExpression nodes in the whole document.
How does this apply to your problem?
//*[contains(./*,'Lockwood')][1]
This selects all elements that have at least one child whose string value contains 'Lockwood' and that are the first such child of their parent. All three elements that have a text node containing the string 'Lockwood' are the first such child of their parents, so the result is that three elements are selected.
//*[contains(./*,'Lockwood')][2]
There is no element that has a child with string value containing the string 'Lockwood' and is the second such child of its parent. No nodes are selected.
//*[contains(./*,'Lockwood')][3]
There is no element that has a child with string value containing the string 'Lockwood' and is the third such child of its parent. No nodes are selected.
Solution:
Use:
(//*[contains(./*,'Lockwood')])[1]
(//*[contains(./*,'Lockwood')])[2]
(//*[contains(./*,'Lockwood')])[3]
Each of these selects exactly the Nth element (N = {1,2,3}) selected by //*[contains(./*,'Lockwood')], correspondingly: BusinesLetter, Recipient and Body.
Remember:
The [] operator has higher priority (precedence) than the // abbreviation.