I am trying to convert XML into desired JSON using Groovy in SAP CPI. I am not getting results as wanted using standard XML-to-JSON converter provided by SAP CPI. I have written below groovy code and it is giving me JSON as I wanted except the Array name.
def xml = """<Rows>
<Values>
<Value>
<Name>DummyTest_1</Name>
<Site.Value>3</Site.Value>
<Description>Dummy PN For Test 1</Description>
<Type.Value>N</Type.Value>
<BuyerCode.Value/>,
<PlannerCode.Value/>,
<PlannerCode.Description/>,
<ABCCode.Value>Default</ABCCode.Value>
<UnitOfMeasure.Value>LB</UnitOfMeasure.Value>
<SafetyStockQty>1</SafetyStockQty>
<StdUnitCost>10</StdUnitCost>
<AverageSellingPrice>15</AverageSellingPrice>
<ProductFamily.Value/>
<ProductGroup1>CHC</ProductGroup1>
<IncrementalRule.Value>Default</IncrementalRule.Value>
<MUEPoolNettingType.Value>Ignore</MUEPoolNettingType.Value>
<PlanningCalendars.Value>Default</PlanningCalendars.Value>
<SourceRule.Value>Default</SourceRule.Value>
</Value>
<Value>
<Name>DummyTest_2</Name>
<Site.Value>4</Site.Value>
<Description>Dummy PN For Test 2</Description>
<Type.Value>H</Type.Value>
<BuyerCode.Value/>,
<PlannerCode.Value/>,
<PlannerCode.Description/>,
<ABCCode.Value>Default2</ABCCode.Value>
<UnitOfMeasure.Value>BL</UnitOfMeasure.Value>
<SafetyStockQty>2</SafetyStockQty>
<StdUnitCost>20</StdUnitCost>
<AverageSellingPrice>16</AverageSellingPrice>
<ProductFamily.Value/>
<ProductGroup1>CHC</ProductGroup1>
<IncrementalRule.Value>Default</IncrementalRule.Value>
<MUEPoolNettingType.Value>Ignore</MUEPoolNettingType.Value>
<PlanningCalendars.Value>Default</PlanningCalendars.Value>
<SourceRule.Value>Default</SourceRule.Value>
</Value>
</Values>
</Rows>"""
def parsed = new XmlSlurper().parseText(xml)
def values = ['Rows' : parsed.'**'.findAll{it.name() == 'Value'}.collect{element -> element.children().breadthFirst()*.name().findAll { !element."$it".children().size() }.collect{element."$it".text()}}]
def fields = ['Fields' : parsed.'**'.find{it.name() == 'Value'}.collect{element -> element.children().breadthFirst()*.name()
.findAll { !element."$it".children().size() }
.collect{element."$it".name()}}]
fields += values
println new groovy.json.JsonBuilder(fields).toPrettyString()
I am getting below output:
{
"Fields": [
[
"Name",
"Site.Value",
"Description",
"Type.Value",
"BuyerCode.Value",
"PlannerCode.Value",
"PlannerCode.Description",
"ABCCode.Value",
"UnitOfMeasure.Value",
"SafetyStockQty",
"StdUnitCost",
"AverageSellingPrice",
"ProductFamily.Value",
"ProductGroup1",
"IncrementalRule.Value",
"MUEPoolNettingType.Value",
"PlanningCalendars.Value",
"SourceRule.Value"
]
],
"Rows": [
[
"DummyTest_1",
"3",
"Dummy PN For Test 1",
"N",
"",
"",
"",
"Default",
"LB",
"1",
"10",
"15",
"",
"CHC",
"Default",
"Ignore",
"Default",
"Default"
],
[
"DummyTest_2",
"4",
"Dummy PN For Test 2",
"H",
"",
"",
"",
"Default2",
"BL",
"2",
"20",
"16",
"",
"CHC",
"Default",
"Ignore",
"Default",
"Default"
]
]
}
But the expected output is :
{
"Fields": [
[
"Name",
"Site.Value",
"Description",
"Type.Value",
"BuyerCode.Value",
"PlannerCode.Value",
"PlannerCode.Description",
"ABCCode.Value",
"UnitOfMeasure.Value",
"SafetyStockQty",
"StdUnitCost",
"AverageSellingPrice",
"ProductFamily.Value",
"ProductGroup1",
"IncrementalRule.Value",
"MUEPoolNettingType.Value",
"PlanningCalendars.Value",
"SourceRule.Value"
]
],
"Rows": [
{
"Value":[
"DummyTest_1",
"3",
"Dummy PN For Test 1",
"N",
"",
"",
"",
"Default",
"LB",
"1",
"10",
"15",
"",
"CHC",
"Default",
"Ignore",
"Default",
"Default"
]
},
{
"Value":[
"DummyTest_2",
"4",
"Dummy PN For Test 2",
"H",
"",
"",
"",
"Default2",
"BL",
"2",
"20",
"16",
"",
"CHC",
"Default",
"Ignore",
"Default",
"Default"
]
}
]
}
I don't know how to populate "Values" Label before array like expected output.
You can transform the elements in values
to Maps with Value
as key. Try to change the line
def values = ['Rows' : parsed.'**'.findAll{it.name() == 'Value'}.collect{element -> element.children().breadthFirst()*.name().findAll { !element."$it".children().size() }.collect{element."$it".text()}}]
to
def values = ['Rows' : parsed.'**'.findAll{it.name() == 'Value'}.collect{element -> [Value: element.children().breadthFirst()*.name().findAll { !element."$it".children().size() }.collect{element."$it".text()}]}]
EDIT
Now that I look at it again, the way you filter by !element."$it".children().size()
may lead to unintended behaviour. I am not sure if that's what you really want. If you want all the leaves under Value
you could try
def values = ['Rows' : parsed.'**'.findAll { it.name() == 'Value' }.collect { element -> [(element.name()): element.'**'.findAll { !it.childNodes() }*.text()] }]
def fields = ['Fields': parsed.'**'.find { it.name() == 'Value' }.collect { element -> element.'**'.findAll { !it.childNodes() }*.name() }]
EDIT2
Just use collectMany
instead of collect
to flatten the inner list in fields
by one level.
def values = ['Rows' : parsed.'**'.findAll { it.name() == 'Value' }.collect { element -> [(element.name()): element.'**'.findAll { !it.childNodes() }*.text()] }]
def fields = ['Fields': parsed.'**'.find { it.name() == 'Value' }.collectMany { element -> element.'**'.findAll { !it.childNodes() }*.name() }]