xquery tranformation to embedded type - xquery

I'm writing an .xq transformation for osb service:
I have the following structure of tags:
<case>
<segment> Earth </segment>
<subSegment> Africa </subSegment>
<complexType>
<param1> values1 </param1>
<param2> values2 </param2>
</complexType>
</case>
I would like to map them correspondingly to
<case>
<complexType>
<segment> Earth </segment>
<subSegment> Africa </subSegment>
<param1> values1 </param1>
<param2> values2 </param2>
</complexType>
</case>
such that if content in segment/subSegment/param1/param2 is empty of these tags are not present (simultaneously) at all, there will be no complexType tags after transformation:
<case>
</case>
So, when I do smth like that:
{let $complexType := $cc/ns1:case/ns2:complexType
return
<ns3:complexType>
{
for $segment in $cc/ns1:case/ns2:segment
return <ns3:segment>{ data($segment) }</ns3:segment>
}
{
for $subSegment in $cc/ns1:case/ns2:subSegment
return <ns3:subSegment>{ data($subSegment) }</ns3:subSegment>
}
{
for $param1 in $complexType/ns2:param1
return <ns3:param1>{ data($param1) }</ns3:param1>
}
{
for $param2 in $complexType/ns2:param2
return <ns3:param2>{ data($param2) }</ns3:param2>
}
}
It almost does the job but in case everything is empty or not present it still returns empty complexType tag:
<case>
<complexType></complexType>
</case>
Please, give me a hint about how to fix this. Thanks!

Bind all your elements to a variable and then you can simply check whether this sequence of elements is empty or not. Something along the lines of this:
{let $complexType := $cc/ns1:case/ns2:complexType
let $elements := (
for $segment in $cc/ns1:case/ns2:segment
return <ns3:segment>{ data($segment) }</ns3:segment>,
for $subSegment in $cc/ns1:case/ns2:subSegment
return <ns3:subSegment>{ data($subSegment) }</ns3:subSegment>,
for $param1 in $complexType/ns2:param1
return <ns3:param1>{ data($param1) }</ns3:param1>,
for $param2 in $complexType/ns2:param2
return <ns3:param2>{ data($param2) }</ns3:param2>
)
return
if (exists($elements))
then <ns3:complexType>{ $elements } </ns3:complexType>
else ()
}

Related

Getting the JSON output in an object format for a single record instead of an array using XQuery

I am getting JSON output from an API in an array format(for multiple records) and in object format for a single record. Instead consumer wants to maintain one format - as an array for single record also. Please let me know if there is a way to display the JSON output in array format irrespective of single/multiple records using XQuery
I have tried the below XQuery:
<Response>
<totalSize>{totalSize/number()}</totalSize>
<done>{done/text()}</done>
<nextRecordsUrl>{nextRecordsUrl/text()}</nextRecordsUrl>
{
let $input:=/root/records
for $i in $input
return
<records>
<Product1>{$i/Product_Lookup_BI__c/text()}</Product1>
<EventLastModifiedDate>{$i/LastModifiedDate/text()}</EventLastModifiedDate>
<Venue>{$i/Venue_vod__r/(fn:concat(fn:concat(fn:concat(fn:concat(fn:concat(fn:concat(BI_Country_Code__c/text(),'-'),State_Province_vod__c/text()),'-'),City_vod__c/text()),'-'),Address_vod__c/text()))}</Venue>
{
let $a:=$i/EM_Event_Team_Member_vod__r/records
for $y in $a
return
<User_records>
<AttendeeLastModifiedDate>{$y/LastModifiedDate/text()}</AttendeeLastModifiedDate>
<EmployeeName>{$y/Team_Member_vod__r/Name/text()}</EmployeeName>
<EmployeeID>{$y/Team_Member_vod__r/BIDS_ID_BI__c/text()}</EmployeeID>
</User_records>
}
</records>
}
</Response>
Actual Output from the above XQuery:
{
"Response": {
"totalSize": 1,
"done": true,
"nextRecordsUrl": "",
"records": {
"Product1": "12345",
"EventLastModifiedDate": "2021-11-10T01:30:55.000+0000",
"Venue": "UK",
"User_records": {
"AttendeeLastModifiedDate": "2021-11-08T02:55:03.000+0000",
"EmployeeName": "Ish",
"EmployeeID": "00002113152"
}
}
}
}
Expected Output:
The Output should be in an array format for "records" & "user_records"
{
"Response":{
"totalSize":1,
"done":true,
"nextRecordsUrl":"",
"records":[
{
"Product1":"12345",
"EventLastModifiedDate":"2021-11-10T01:30:55.000+0000",
"Venue":"UK",
"User_records":[
{
"AttendeeLastModifiedDate":"2021-11-08T02:55:03.000+0000",
"EmployeeName":"Ish",
"EmployeeID":"00002113152"
}
]
}
]
}
}
Try:
<User_records xmlns:json="http://www.json.org" json:array="true">
<AttendeeLastModifiedDate>{$y/LastModifiedDate/text()}</AttendeeLastModifiedDate>
<EmployeeName>{$y/Team_Member_vod__r/Name/text()}</EmployeeName>
<EmployeeID>{$y/Team_Member_vod__r/BIDS_ID_BI__c/text()}</EmployeeID>
</User_records>
I would do the same for <records> as well. This example works in eXist-db. The JSON namespace may be different in your environment.
Here is what I ran in eXide:
xquery version "3.1";
declare option exist:serialize "method=json indent=yes media-type=application/json";
<Response>
<totalSize>5</totalSize>
<done>yes</done>
<nextRecordsUrl>abc</nextRecordsUrl>
<User_records xmlns:json="http://www.json.org" json:array="true">
<AttendeeLastModifiedDate>123</AttendeeLastModifiedDate>
<EmployeeName>456</EmployeeName>
<EmployeeID>789</EmployeeID>
</User_records>
</Response>

Using XQUERY (baseX) I want information inside some of Jasper reports. I need to return text inside ELEMENT cdata

using notepad++ I have the path to the element containing the cdata showing as this:
/jasperReport[1]/columnHeader[1]/band[1]/staticText[1]/text[1]
the source looks similar to this:
<jasperReport>
<columnHeader>
<band height="22" >
<staticText>
<reportElement x="0" y="0" width="48" height="20" uuid="one"/>
<box leftPadding="1">
<pen lineWidth="0.5"/>
<topPen lineWidth="0.5"/>
<leftPen lineWidth="0.5"/>
<bottomPen lineWidth="0.5"/>
<rightPen lineWidth="0.5"/>
</box>
<textElement>
<font size="8" />
</textElement>
<text><![CDATA[MYNUM]]></text>
</staticText>
I'm trying to use XQUERY to return values similar to "MYNUM" second to last line in xml fragment.
In this case it will tell me that this particular part of the header has the text "MYNUM" in it. I intend on returning more complete information for many similar reports using XQUERY with BaseX on my mac.
So I could run xquery on a set of report files that would output something like:
/filenameofreport.data
<Header>
<reportElement>x=0 y=0 value=MYNUM</reportElement>
<reportElement>x=10 y=0 value=THATNUM</reportElement>
</HEADER>
ok I know this input query:
declare namespace abc="http://jasperreports.sourceforge.net/jasperreports";
for $document in collection("temp")
where document-uri($document) = "/temp/Daily_P1NoAssign_.data"
return
for $thefield in $document//abc:jasperReport[1]/abc:columnHeader[1]/abc:band[1]/abc:staticText[1]/abc:textElement[1]/abc:font
let $fontsize := string-join(("<font>",$thefield/#size,"</font>"))
let $docgroup := xs:string((document-uri($document)))
group by $docgroup
order by $docgroup
return ( element {"jasperReport"} { $docgroup } , $fontsize)
produced this output:
<jasperReport>/temp/Daily_P1NoAssign_.data</jasperReport>
<font>8</font>
So ...
this input targeted the sibling of textElement should have the right element, but I don't know how to output the data
xquery:
declare namespace abc="http://jasperreports.sourceforge.net/jasperreports";
for $document in collection("temp")
where document-uri($document) = "/temp/Daily_P1NoAssign_.data"
return
for $thefield in $document//abc:jasperReport[1]/abc:columnHeader[1]/abc:band[1]/abc:staticText[1]/abc:text[1]
let $textinfo := string-join(("<text>",$thefield/*,"</text>"))
let $docgroup := xs:string((document-uri($document)))
group by $docgroup
order by $docgroup
return ( element {"jasperReport"} { $docgroup } , $textinfo)
returns:
<jasperReport>/temp/Daily_P1NoAssign_.data</jasperReport>
<text></text>
update:
Oh I almost got it all figured out I think - first here is the solution so far, but I would like to output the data slightly differently...
declare namespace abc="http://jasperreports.sourceforge.net/jasperreports";
for $document in collection("temp")
where document-uri($document) = "/temp/Daily_P1NoAssign_.data"
return
for $thefield in $document//abc:jasperReport[1]/abc:columnHeader[1]/abc:band[1]/abc:staticText/abc:text[1]
let $textinfo3 := $thefield/text()
let $docgroup := xs:string((document-uri($document)))
group by $docgroup
order by $docgroup
return ( element {"jasperReport"} { $docgroup } ,$textinfo3)
returns this with WONUM WORKTPE etc on separate lines:
<jasperReport>/temp/Daily_P1NoAssign_.data</jasperReport>
WONUM
WORKTYPE
LOCATION
MODELNUM
SERIALNUM
REPORTDATE
STATUSDATE
STATUS
OWNER
OWNERGROUP
WARRANTYEXPDATE
INWARRANTY
Is there a way to return this with WONUM WORKTYPE etc all on one line??
<jasperReport>/temp/Daily_P1NoAssign_.data</jasperReport>
WONUM WORKTYPE LOCATION MODELNUM SERIALNUM REPORTDATE STATUSDATE STATUS OWNER OWNERGROUP WARRANTYEXPDATE INWARRANTY
The easiest way to eliminate the linefeed characters would be to use the normalize-space() method.
Either
let $textinfo3 := normalize-space($thefield/text())
or
let $textinfo3 := normalize-space(string-join($thefield/text(), " "))

XQuery - Enclosing sequence of elements under a single node

I'm using xquery with BaseX to attempt to retrieve information from JMeter test cases (.jmx files) in a format I desire.
This is the code I'm actually running (inside BaseX GUI):
let $rlist := db:list("JMeter")
for $resource in $rlist
let $rcontent := db:open("JMeter", $resource)
let $ret :=
<HttpRequest>
{
for $item in $rcontent/jmeterTestPlan//HTTPSamplerProxy
return
(<method>
{
$item/stringProp[attribute()/string() = "HTTPSampler.method"]/text()
}
</method>,
<path>
{
let $parse := $item/stringProp[attribute()/string() = "HTTPSampler.path"]/text()
let $parse := fn:replace($parse, "\?[^/]*", "")
let $parse := fn:replace($parse, "\$[^/]*", "\${}")
let $parse := fn:replace($parse, "[0-9]+", "\${}")
return $parse
}
</path>,
<resource>
{
$resource
}
</resource>)
}
</HttpRequest>
return
<HttpRequests>
{
$ret
}
</HttpRequests>
The resulting xml looks like this:
<HttpRequests>
<HttpRequest>
<method>POST</method>
<path>/config</path>
<resource>CentralConfiguration/Requests/addConfiguration.jmx</resource>
</HttpRequest>
</HttpRequests>
<HttpRequests>
<HttpRequest>
<method>POST</method>
<path>/propertyType</path>
<resource>CentralConfiguration/Requests/addPropertyType.jmx</resource>
</HttpRequest>
</HttpRequests>
...
This is what I expect the result to look like:
<HttpRequests>
<HttpRequest>
<method>POST</method>
<path>/config</path>
<resource>CentralConfiguration/Requests/addConfiguration.jmx</resource>
</HttpRequest>
<HttpRequest>
<method>POST</method>
<path>/propertyType</path>
<resource>CentralConfiguration/Requests/addPropertyType.jmx</resource>
</HttpRequest>
...
</HttpRequests>
I'd much appreciate help in formatting my xquery to get the expected result, thanks
The problem was solved by placing the outside for loop inside the first label.

XQuery with if condition in for loop

I have written xquery to return results in normal way.
let $results := //data:data
return
<result>
{
for $i in $results
return
<documentInformation>
<id>{data($i/DATA:ID)}</id>
<status>{data($i/#status)}</status>
<title>{data($i/data:title)}</title>
<displayName>{data($i/DATA:DISPLAYNAME)}</displayName>
</documentInformation>
}
</result>
Now, I have to filter out the results in for loop with some condition like
(pseudo logic)
if id = 'abc' and status ="closed"
then skip the row
else add row.
I have tried several ways. but could not run the query..
Try this:
<result>
{
for $i in //data:data
where fn:not($i/DATA:ID = 'abc' and $i/#status = "closed")
return
<documentInformation>
<id>{data($i/DATA:ID)}</id>
<status>{data($i/#status)}</status>
<title>{data($i/data:title)}</title>
<displayName>{data($i/DATA:DISPLAYNAME)}</displayName>
</documentInformation>
}
</result>
Note that the XPath //data:data may have a lot of work to do, but that's a separate matter.
You Can also use if condition instead of where
<result>
{
for $i in //data:data
return
if($i/DATA:ID != 'abc' and $i/#status != "closed")
then
(
<documentInformation>
<id>{data($i/DATA:ID)}</id>
<status>{data($i/#status)}</status>
<title>{data($i/data:title)}</title>
<displayName>{data($i/DATA:DISPLAYNAME)}</displayName>
</documentInformation>
)
else ()
}
</result>

wrapping child node content in xquery

I have what I hope is a really simple question, but I'm a novice at xquery and I can't make this work:
I have the following bit of xml:
<collation>1<num>12</num> 2<num>12</num> ||
I<num>8</num>-V<num>8</num>, 1 flyleaf</collation>
That I need to transform so that becomes the content of a new node, like so:
<note displayLabel="Collation: Notes">1(12) 2(12) || I(8)-V(8), 1 flyleaf<note>
I am using the following xquery code to attempt to do this:
<note displayLabel="Collation:Notes">{for $t in doc("collation.xml")//collation,
$h in distinct-values($t)
return
????
</note>
The problem is that I can either display all of the content (so without the parentheses) using data($t), or I can display just the things I want to be in parentheses (the information in the tags) using data($t/num) but I can't figure out how to display both with the items in tags wrapped in parentheses. I'm sure it's a really simple answer but I can't find it.
This is a good job for recursion:
declare function local:render(
$n as node()
) as node()?
{
typeswitch($n)
case element(num) return text{concat('(', $n, ')')}
case element(collation) return
<note displayLabel="Collation: Notes">{
for $n in $n/node()
return local:render($n)
}</note>
case element() return element { node-name($n) } {
for $n in $n/node()
return local:render($n)
}
default return $n
};
local:render(
<collation>1<num>12</num> 2<num>12</num> || I<num>8</num>-V<num>8</num>, 1 flyleaf</collation>)
=>
<note displayLabel="Collation: Notes">1(12) 2(12) || I(8)-V(8), 1 flyleaf</note>

Resources