Groovy MarkupBuilder : How to create markup and append string - groovy

I am using the Groovy MarkupBuilder to create some XML.
In my scenario I get an xml string which is essentially the body of the xml document and then I want to surround it with some other stuff.
Something like this....
def xmltext = '''<node><name short="yes">tim</name><fun>maybe</fun></node>'''
def body = new XmlSlurper(false,false).parseText( xmltext )
def writer = new StringWriter()
def xml = new MarkupBuilder(writer)
xml.mkp.xmlDeclaration version: '1.0', encoding: 'UTF-8'
xml.'cre:InputParamters'('xmlns:cre': 'http://xmlns.oracle.com/apps/ar/soaprovider/plsql/ar_invoice_api_pub/create_single_invoice/') {
xml.'cre:P_P_TRX_HEADER_TBL' {
xml.'cre:P_TRX_HEADER_TBL_ITEM' {
map 'cre:TRX_HEADER_ID', '',xml
map 'cre:TRX_DATE', requestPayload?.invoiceDate, xml
map 'cre:TRX_CURRENCY', requestPayload?.currencyCode, xml
map 'cre:TRX_CLASS', 'INV', xml
map 'cre:CUST_TRX_TYPE_ID', '1034', xml
map 'cre:BILL_TO_CUSTOMER_ID', '147055', xml
}
}
<<APPEND ELEMENTS FROM XML STRING HERE>>
}
private map (Object field, Object value, MarkupBuilder xml) {
if (value) {
xml."$field"(value)
}
}
return writer.toString()
Could someone help me with the bit 'APPEND ELEMENTS FROM XML STRING HERE' and how to get that the work for me?
thanks

Here is the changed script:
Note that you have used some variable to get the values. To demonstrate I have used fixed values below which you may be able to replace it.
import groovy.xml.*
def xmltext = '''<node><name short="yes">tim</name><fun>maybe</fun></node>'''
def builder = new StreamingMarkupBuilder()
builder.encoding = 'UTF-8'
def xml = builder.bind {
mkp.xmlDeclaration()
namespaces << [cre:'http://xmlns.oracle.com/apps/ar/soaprovider/plsql/ar_invoice_api_pub/create_single_invoice/']
cre.InputParamters{
cre.P_P_TRX_HEADER_TBL {
cre.P_TRX_HEADER_TBL_ITEM {
cre.TRX_HEADER_ID ('')
cre.TRX_DATE ('2017-02-17')
cre.TRX_CURRENCY( 'USD')
cre.TRX_CLASS('INV')
cre.CUST_TRX_TYPE_ID( '1034')
cre.BILL_TO_CUSTOMER_ID( '147055')
}
}
mkp.yieldUnescaped xmltext
}
}
println XmlUtil.serialize(xml)
You may try it quickly online Demo
Output

Related

passing in args to a Closure for a StreamingMarkupBuilder

Groovy 2.4, Spring 5.3.13
Not having much luck using StreamingMarkupBuilder to create some XML, serialize it and print it
public void createMsgToStreamOut( String strCreatedAt, String strEntity, String strIdNum, String strEvent) {
def streamBuilder = new StreamingMarkupBuilder();
streamBuilder.encoding = "UTF-8"
def xml = streamBuilder.bind{ strCreatedAt, strEntity, strIdNum, strEvent ->
>> some magic goes here
}
def xmlStr = XmlUtil.serialize( xml)
println xmlStr;
}
createMsgToStreamOut( "2022-09-10T12:13:14.567", "Matter", "907856", "create");
should give
<?xml version="1.0" encoding="UTF-8"?>
<message>
<timestamp>2022-09-10T12:13:14.567</timestamp>
<entity>Matter</entity>
<number>907856</number>
<event>create</event>
</message>
next step is to stream the output to a Kafka producer.
The magic you're looking for looks like that, I suppose:
def xml = streamBuilder.bind {
message {
timestamp(strCreatedAt)
entity(strEntity)
number(strIdNum)
event(strEvent)
}
}
Here is the fully working script:
import groovy.xml.*
createMsgToStreamOut( "2022-09-10T12:13:14.567", "Matter", "907856", "create");
void createMsgToStreamOut(String strCreatedAt, String strEntity, String strIdNum, String strEvent) {
def streamBuilder = new StreamingMarkupBuilder();
streamBuilder.encoding = "UTF-8"
def xml = streamBuilder.bind {
message {
timestamp(strCreatedAt)
entity(strEntity)
number(strIdNum)
event(strEvent)
}
}
def xmlStr = XmlUtil.serialize( xml)
println xmlStr;
}
Let me know if it helps.

How to get a value of a dynamic key in Groovy JSONSlurper?

The variable resp contains below JSON response -
{"name":"sample","address":{"country":"IN","state":"TN","city":"Chennai"}}
I have planned using param1 variable to get the required key from JSON response, but I'm unable to get my expected results.
I'm passing the param1 field like - address.state
def actValToGet(param1){
JsonSlurper slurper = new JsonSlurper();
def values = slurper.parseText(resp)
return values.param1 //values.address.state
}
I'm getting NULL value here -> values.param1
Can anyone please help me. I'm new to Groovy.
The map returned from the JsonSlurper is nested rather than than flat. In other words, it is a map of maps (exactly mirroring the Json text which was parsed). The keys in the first map are name and address. The value of name is a String; the value of address is another map, with three more keys.
In order to parse out the value of a nested key, you must iterate through each layer. Here is a procedural solution to show what's happening.
class Main {
static void main(String... args) {
def resp = '{"name":"sample","address":{"country":"IN","state":"TN","city":"Chennai"}}'
println actValToGet(resp, 'address.state')
}
static actValToGet(String resp, String params){
JsonSlurper slurper = new JsonSlurper()
def values = slurper.parseText(resp)
def keys = params.split(/\./)
def output = values
keys.each { output = output.get(it) }
return output
}
}
A more functional approach might replace the mutable output variable with the inject() method.
static actValToGet2(String resp, String params){
JsonSlurper slurper = new JsonSlurper()
def values = slurper.parseText(resp)
def keys = params.split(/\./)
return keys.inject(values) { map, key -> map.get(key) }
}
And just to prove how concise Groovy can be, we can do it all in one line.
static actValToGet3(String resp, String params){
params.split(/\./).inject(new JsonSlurper().parseText(resp)) { map, key -> map[key] }
}
You may want to set a debug point on the values output by the parseText() method to understand what it's returning.

Groovy map constructor keys to different variable names

I have JSON looking like:
{
"days": [
{
"mintemp": "21.8"
}
]
}
With Groovy, I parse it like this:
class WeatherRow {
String mintemp
}
def file = new File("data.json")
def slurper = new JsonSlurper().parse(file)
def days = slurper.days
def firstRow = days[0] as WeatherRow
println firstRow.mintemp
But actually, I would like to name my instance variable something like minTemp (or even something completely random, like numberOfPonies). Is there a way in Groovy to map a member of a map passed to a constructor to something else?
To clarify, I was looking for something along the lines of #XmlElement(name="mintemp"), but could not easily find it:
class WeatherRow {
#Element(name="mintemp")
String minTemp
}
Create a constructor that takes a map.
Runnable example:
import groovy.json.JsonSlurper
def testJsonStr = '''
{"days": [
{ "mintemp": "21.8" }
]}'''
class WeatherRow {
String minTemp
WeatherRow(map) {
println "Got called with constructor that takes a map: $map"
minTemp = map.mintemp
}
}
def slurper = new JsonSlurper().parseText(testJsonStr)
def days = slurper.days
def firstRow = days[0] as WeatherRow
println firstRow.minTemp
Result:
Got called with constructor that takes a map: [mintemp:21.8]
21.8
(of course you'd remove the println line, it's just there for the demo)
You can achieve this using annotation and simple custom annotation processor like this:
1. Create a Custom Annotation Class
#Retention(RetentionPolicy.RUNTIME)
#interface JsonDeserializer {
String[] names() default []
}
2. Annotate your instance fields with the custom annotation
class WeatherRow{
#JsonDeserializer(names = ["mintemp"])
String mintemp;
#JsonDeserializer(names = ["mintemp"])
String minTemp;
#JsonDeserializer(names = ["mintemp"])
String numberOfPonies;
}
3. Add custom json deserializer method using annotation processing:
static WeatherRow fromJson(def jsonObject){
WeatherRow weatherRow = new WeatherRow();
try{
weatherRow = new WeatherRow(jsonObject);
}catch(MissingPropertyException ex){
//swallow missing property exception.
}
WeatherRow.class.getDeclaredFields().each{
def jsonDeserializer = it.getDeclaredAnnotations()?.find{it.annotationType() == JsonDeserializer}
def fieldNames = [];
fieldNames << it.name;
if(jsonDeserializer){
fieldNames.addAll(jsonDeserializer.names());
fieldNames.each{i ->
if(jsonObject."$i")//TODO: if field type is not String type custom parsing here.
weatherRow."${it.name}" = jsonObject."$i";
}
}
};
return weatherRow;
}
Example:
def testJsonStr = '''
{
"days": [
{
"mintemp": "21.8"
}
]
}'''
def parsedWeatherRows = new JsonSlurper().parseText(testJsonStr);
assert WeatherRow.fromJson(parsedWeatherRows.days[0]).mintemp == "21.8"
assert WeatherRow.fromJson(parsedWeatherRows.days[0]).minTemp == "21.8"
assert WeatherRow.fromJson(parsedWeatherRows.days[0]).numberOfPonies == "21.8"
Check the full working code at groovyConsole.

How to add a node to groovy MarkupBuilder?

I am using MarkupBuilder to generate xml, need to know how can I add NodeChild to a MarkupBuilder Object
my code
def fxml=new File("E:\\Projects\\dom.xml")
def xmltext=new XmlSlurper(false,false).parseText(fxml.text)
or
def xml=new XmlSlurper(false,false).parse("E:\\Projects\\dom.xml")
def abc = new groovy.xml.MarkupBuilder()
abc.product(name:"Dota"){
language("Java")
language("Groovy")
language("JavaScript")
domainsinfa{delegate.current.appendNode( xmltext)}
}
You can use StreamingMarkupBuilder to insert arbitrary nodes to the xml:
import groovy.xml.*
def xmltext = '''<node><name short="yes">tim</name><fun>maybe</fun></node>'''
def xml = new XmlSlurper( false, false ).parseText( xmltext )
def newxml = new StreamingMarkupBuilder().bind {
product(name:"Dota") {
language("Java")
language("Groovy")
language("JavaScript")
mkp.yield xml
}
}
println XmlUtil.serialize( newxml )

Groovy Xml Parsing with namespaces

I've been trying to do some xml modifications with groovy's XML Slurper.
Basically, i'm going through the xml and looking for tags or attributes that have ? as the value and then replacing it with some value.
I've got it working for xml that doesn't have namespaces but once I include them things get wonky. For example, this:
String foo = "<xs:test xmlns:xs="http://schemas.xmlsoap.org/soap/envelope/"
xmlns:foo="http://myschema/xmlschema" name='?'>
<foo:tag1>?</foo:tag1>
<foo:tag2>?</foo:tag2>
</xs:test>";
produces:
<Envelope/>
Here's the groovy code I'm using. This does appear to work when I am not using a namespace:
public def populateRequest(xmlString, params) {
def slurper = new XmlSlurper().parseText(xmlString)
//replace all tags with ?
def tagsToReplace = slurper.depthFirst().findAll{ foundTag ->
foundTag.text() == "?"
}.each { foundTag ->
foundTag.text = {webServiceOperation.parameters[foundTag.name()]}
foundTag.replaceNode{
"${foundTag.name()}"(webServiceOperation.parameters[foundTag.name()])
}
}
//replace all attributes with ?
def attributesToReplace = slurper.list().each{
it.attributes().each{ attributes ->
if(attributes.value == '?')
{
attributes.value = webServiceOperation.parameters[attributes.key]
}
}
}
new StreamingMarkupBuilder().bind { mkp.yield slurper }.toString()
}
from groovy documentation
def wsdl = '''
<definitions name="AgencyManagementService"
xmlns:ns1="http://www.example.org/NS1"
xmlns:ns2="http://www.example.org/NS2">
<ns1:message name="SomeRequest">
<ns1:part name="parameters" element="SomeReq" />
</ns1:message>
<ns2:message name="SomeRequest">
<ns2:part name="parameters" element="SomeReq" />
</ns2:message>
</definitions>
'''
def xml = new XmlSlurper().parseText(wsdl).declareNamespace(ns1: 'http://www.example.org/NS1', ns2: 'http://www.example.org/NS2')
println xml.'ns1:message'.'ns1:part'.size()
println xml.'ns2:message'.'ns2:part'.size()

Resources