How to read values from JSON embedded in XML with XSLT? - json

This is my XML, which I want to convert into DOT:
<Import>
<Row>
<id>1</id>
<parentmenu>siasn-instansi</parentmenu>
<label>Layanan Profile ASN</label>
<role_id>1</role_id>
<role>role:siasn-instansi:profilasn:viewprofil</role>
<items>[{"url": "/tampilanData/pns", "label": "Profile Pegawai", "subMenu": "pns"}, {"url": "/tampilanData/pppk", "label": "Profile Pegawai PPPK", "subMenu": "pppk"}, {"url": "/tampilanData/JPTNonASN", "label": "Profile Pegawai PPT Non-ASN", "subMenu": "ppt"}]</items>
</Row>
</Import>
Below is a picture of my XSL code with the DOT file rules.
XSL code:
The problem is, I want to get the values from <items>, like below:
/displayData/pns
/displayData/pppk
/displayData/JPTNoASN
and I want to take the value points above into my XSL as outlined in red in the image.
How would the XSL look like that can take the values from my XML? The <items> value is quite difficult, unlike the <role> values, which I have managed to take.

In XSLT 3.0 you can do, for example:
<xsl:template match="items">
<xsl:variable name="content" select="parse-json(.)/*" as="map(*)*"/>
<xsl:for-each select="$content">
url="{?url}"
label="{?label}"
menu="{?subMenu}"
</xsl:for-each>
</xsl:template>
The parse-json() function returns an array of maps; the "/*" operator turns this into a sequence of maps; the construct ?url accesses a specific entry in a map.

Related

XSL: How to concat additional string when setting a HTML table class

Is there a way to concat some name to a class with a variable?
<table style="display:none;border-style:solid;">
<xsl:attribute name="class">
<xsl:value-of select="BookName"/>
</xsl:attribute>
This piece of my code would name a class by a value of "BookName" from XML, but I need somehow to concat it with just a static text "booktable", meaning that BookName would be some value, but booktable is always static text for example result would be class="NewEncouters2009 booktable"
Simply use
<table style="display:none;border-style:solid;" class="{BookName} booktable">
As for xsl:attribute, you can of course put in any static text there e.g.
<xsl:attribute name="class"><xsl:value-of select="BookName"/> booktable</xsl:attribute>

xslt escape angle brackets

I would like to replace a '.\s+\w+' sequence with a literal
Here is what I have:
<xsl:value-of select="replace($fdesc,'[.][ ]+\w+','<br/>')" />
Here is what I get as an error:
Error on line 33 column 57 of file:/Users/seth/Documents/EmausCCB/XSL/form_list.xsl:
SXXP0003: Error reported by XML parser: The value of attribute "select" associated with an
element type "null" must not contain the '<' character.
Failed to compile stylesheet. 1 error detected.
If I do :
<xsl:value-of select="replace($fdesc,'[.][ ]+\w+','br')" />
I get the expected results
Well if you want to create a br result element you can't use the replace function as it returns a string and not elements.
Use analyze-string instead e.g.
<xsl:analyze-string select="$fesc" regex="[.][ ]+\w+">
<xsl:matching-substring>
<br/>
</xsl:matching-substring>
<xsl:non-matching-substring>
<xsl:value-of select="."/>
</xsl:non-matching-substring>
</xsl:analyze-string>
As for the XML parse error, the right syntax would be <xsl:value-of select="replace($fdesc,'[.][ ]+\w+','<br/>')" />, but that would still return a string and not an element node.

Extract data from html/xml

I'm using Webharvest to retrieve data from websites. It converts the html pages to xml documents before getting for me the wanted data based on the xPath provided.
Now I'm working on a page like this: pastebin Where I showed the blocks I'd like to get. Each block should be returned as a single unit.
the xPath the first element of the block is: //div[#id="layer22"]/b/span[#style="background-color: #FFFF99"]
I tested it and it gives all "bloc start" elements.
the xPath of the last element of the block is: //div[#id="layer22"]/a[contains(.,"Join")]
I tested it and it gives all the "bloc end" elements.
The xPath should return a set of blocks as:
(xPath)[1] = block 1
(xPath)[2] = block 2
....
Thank you in advance
Use (for the first wanted result):
($first)[1] | ($last)[1]
|
($first)[1]/following::node()
[count(.|($last)[1]/preceding::node()) = count(($last)[1]/preceding::node())]
where you need to substitute $first with:
//div[#id="layer22"]/b/span[#style="background-color: #FFFF99"]
and substitute $last with:
//div[#id="layer22"]/a[contains(.,"Join")]
To get the k-th result, substitute in the final expression ($first)[1] with ($first)[{k}] and ($last)[1] with ($last)[{k}], where {k} should be replaced by the number k.
This technique follows directly from the well-known Kayessian formula for set intersection in XPath 1.0:
$ns1[count(.|$ns2) = count($ns2)]
which selects the intersection of the two node-sets $ns1 and $ns2 .
Here is XSLT verification with a simple example:
<nums>
<num>01</num>
<num>02</num>
<num>03</num>
<num>04</num>
<num>05</num>
<num>06</num>
<num>07</num>
<num>03</num>
<num>07</num>
<num>10</num>
</nums>
This transformation:
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output omit-xml-declaration="yes" indent="yes"/>
<xsl:variable name="v1" select=
"(//num[. = 3])[1]/following-sibling::*"/>
<xsl:variable name="v2" select=
"(//num[. = 7])[1]/preceding-sibling::*"/>
<xsl:template match="/">
<xsl:copy-of select=
"$v1[count(.|$v2) = count($v2)]"/>
</xsl:template>
</xsl:stylesheet>
applies the XPath expression and the selected nodes are copied to the output:
<num>04</num>
<num>05</num>
<num>06</num>

xsl generate-id() function returns same id twice for different nodes

I have an input xml for a transformation like ;
<?xml version="1.0" encoding="UTF-8" ?>
<AssetcustomerCollection xmlns="http://xmlns.oracle.com/pcbpel/adapter/db/top/somens">
<Assetcustomer xmlns="">
....
</Assetcustomer>
<Assetcustomer xmlns="">
<accountklantid>000000123456789</accountklantid>
<accountrowid>1-W8HQ1J</accountrowid>
<adrestypeaccnt/>
<adrestypecon/>
<assetbankcode>1173</assetbankcode>
<assetnumber>0000001234</assetnumber>
<assetprodcode>1200</assetprodcode>
<assetproduct>Overeenkomst Rekening-courant</assetproduct>
<assetproductlocatie>00</assetproductlocatie>
<assetstatus>Actief</assetstatus>
<assetsubstatus>Lopende rekening</assetsubstatus>
<assettypecode>0010</assettypecode>
<contactklantid/>
<contactrowid/>
<primairaccount>Y</primairaccount>
<primaircontact>N</primaircontact>
<reltypeaccnt>Hoofdcontractant</reltypeaccnt>
<reltypecon/>
<rowidasset>1-X3XBMO</rowidasset>
<rowidassetaccnt>1-X3XBMQ</rowidassetaccnt>
<rowidassetcon/>
<tnsidaccnt/>
<tnsidcon/>
</Assetcustomer>
<Assetcustomer xmlns="">
....
</Assetcustomer>
<Assetcustomer xmlns="">
<accountklantid/>
<accountrowid/>
<adrestypeaccnt/>
<adrestypecon/>
<assetbankcode>1173</assetbankcode>
<assetnumber>0000004321</assetnumber>
<assetprodcode>1201</assetprodcode>
<assetproduct>WereldPas (Zakelijk)</assetproduct>
<assetproductlocatie>00</assetproductlocatie>
<assetstatus>Actief</assetstatus>
<assetsubstatus>Lopende rekening</assetsubstatus>
<assettypecode>0003</assettypecode>
<contactklantid>000000987654321</contactklantid>
<contactrowid>1-X17PLM</contactrowid>
<primairaccount>N</primairaccount>
<primaircontact>Y</primaircontact>
<reltypeaccnt/>
<reltypecon>Pasverantwoordelijke</reltypecon>
<rowidasset>1-X3XBN0</rowidasset>
<rowidassetaccnt/>
<rowidassetcon>1-X3XBNE</rowidassetcon>
<tnsidaccnt/>
<tnsidcon/>
</Assetcustomer>
<Assetcustomer xmlns="">
....
</Assetcustomer>
</AssetcustomerCollection>
When transforming this input xml i got an unexpected output (15 of the 16 input Assetcustomer nodes were transformed) I now have found the cause, but cannot explain why it occurs;
The following transformation returns the same id twice;
<xsl:element name="A">
<xsl:value-of select="generate-id(key('AssetRowIDs',/ns0:AssetcustomerCollection/Assetcustomer[rowidasset = '1-X3XBMO']/*)[1])"/>
</xsl:element>
<xsl:element name="B">
<xsl:value-of select="generate-id(key('AssetRowIDs',/ns0:AssetcustomerCollection/Assetcustomer[rowidasset = '1-X3XBN0']/*)[1])"/>
</xsl:element>
<A>N10211</A>
<B>N10211</B>
While the generated id for any other node with a different rowidasset is different.
Any ideas before i start pulling my hair out ?
Peter
I do not know exactly why , but changing
<xsl:key name="AssetRowIDs" match="Assetcustomer" use="rowidasset"/>
into
<xsl:key name="AssetRowIDs" match="Assetcustomer" use="concat('-',rowidasset,'-')"/>
and
<xsl:for-each select="/ns0:AssetcustomerCollection/Assetcustomer[generate-id() = generate-id(key('AssetRowIDs',rowidasset)[1])]">
into
<xsl:for-each select="/ns0:AssetcustomerCollection/Assetcustomer[generate-id() = generate-id(key('AssetRowIDs',concat('-',rowidasset,'-'))[1])]">
Seems to generate a unique id for each node, still bugging me dat i do not understand the cause of it.
Check the namespace? If the ns0 prefix is bound to a wrong namespace URI, your query will in both cases yield an empty result set. Together with the same first argument for key, that, I imagine, will yield the same call to key() and thus the same ID.
Also I don't think the key() function does what you think it does: http://www.w3schools.com/xsl/func_key.asp
In any case you can apply generate-id() directly on the node set for which you wish to calculate the ID.

xsl any symbol code for value comparison using <xsl:when test

I was wondering what would be any symbol code?
<xsl:when test="path/path1 = '(ANYSYMBOL)1' ">
this code alows us to check if some values equals X1,#1,%1,91....
so what is the anysymbol/ anychar code #xxx?
There's no such wildcards.
You have two options:
<xsl:when test="substring(path/path1, 2) = '1'">
and
<xsl:when test="matches(path/path1, '.1')">
The latter one using regexp is only XSLT 2.0 compatible.