Reputation: 575
I need to convert XML to Java objects. I chose Declarative Stream Mapping (DSM) for this, but it is not clear how to map the nested element with attributes to the list for me. Specifically, how to describe in the YAML file for the next piece:
<categories>
<category id="1" parentId="2">Lorum Ipsum</category>
</categories>
I tried all the options, but they were all unsuccessful.
Below are the pieces of code that I am running.
DSM (yaml):
result:
type: object
path: /catalog/shop
fields:
name:
path: name
company:
path: company
url:
path: url
platform:
path: platform
agency:
path: agency
email:
path: email
currencies:
type: array
path: currencies/currency
fields:
id:
xml:
attribute: true
rate:
xml:
attribute: true
categories:
type: array
path: categories/category
fields:
id:
xml:
attribute: true
parentId:
xml:
attribute: true
deliveryOptions:
type: array
path: delivery-options/option
fields:
cost:
xml:
attribute: true
days:
xml:
attribute: true
offers:
type: array
path: offers/offer
fields:
id:
xml:
attribute: true
available:
xml:
attribute: true
type:
xml:
attribute: true
param:
path: param
offer:
fields:
url:
path: url
price:
path: price
# shortened due to length
XML file to convert:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE yml_catalog SYSTEM "shops.dtd">
<catalog date="2019-09-08 22:37">
<shop>
<name>company name</name>
<company>Microsoft</company>
<url>https://microsoft.com</url>
<platform>woefjwoefi</platform>
<agency>Agent</agency>
<email>[email protected]</email>
<currencies>
<currency id="USD" rate="1"/>
</currencies>
<categories>
<category id="398" parentId="198">Lorum Ipsum</category>
<category id="409">Uncategorized</category>
</categories>
<offers>
<offer id="2016" available="true" type="vendor.model">
<url>https://ms.com/id/1</url>
<price>35.00</price>
<currencyId>USD</currencyId>
<categoryId>168</categoryId>
<picture>https://ms.com/image/cache/catalog/8987-600x600.jpg</picture>
<store>true</store>
<pickup>true</pickup>
<delivery>true</delivery>
<vendor>MS</vendor>
<vendorCode>MS-10111</vendorCode>
<model>plasdf</model>
<downloadable>false</downloadable>
<param name="Weight">0.0</param>
</offer>
<offer id="2017" available="true" type="vendor.model">
<url>https://ms.com/id/2</url>
<price>250.00</price>
<currencyId>USD</currencyId>
<categoryId>201</categoryId>
<picture>https://ms.com/image/cache/catalog/aktiv-600x600.png</picture>
<store>true</store>
<pickup>true</pickup>
<delivery>true</delivery>
<vendor>MS</vendor>
<vendorCode>MS03238</vendorCode>
<model>woeifjwoef wefw fw we</model>
<description>wiefwof wef</description>
<downloadable>false</downloadable>
<weight>30.0</weight>
<param name="Weight">30.0</param>
</offer>
</offers>
</shop>
</catalog>
Java:
public class Shop {
private String name;
private String company;
private String url;
private String platform;
private String agency;
private String email;
private List<Currencies> currencies;
private List<Categories> categories;
private List<DeliveryOptions> deliveryOptions;
private List<Offers> offers;
// getters/setters
}
class Currencies {
// getters/setters
}
class Categories {
// getters/setters
}
class DeliveryOptions {
// getters/setters
}
class Offers {
// getters/setters
}
Output:
{
"name" : "company name",
"company" : "Microsoft",
"url" : "https://microsoft.com",
"platform" : "woefjwoefi",
"agency" : "Agent",
"email" : "[email protected]",
"currencies" : null,
"categories": null,
"deliveryOptions": null,
"offers": null
}
Upvotes: 0
Views: 352
Reputation: 1115
I made some slight indentation adjustement and your dsm config file worked just fine.
DSM.yml
result:
type: object
path: /catalog/shop
fields:
name:
path: name
company:
path: company
url:
path: url
platform:
path: platform
agency:
path: agency
email:
path: email
currencies:
type: array
path: currencies/currency
fields:
id:
xml:
attribute: true
rate:
xml:
attribute: true
categories:
type: array
path: categories
xml:
path: categories/category
fields:
name:
path: ./
id:
xml:
attribute: true
parentId:
xml:
attribute: true
deliveryOptions:
type: array
path: delivery-options/option
fields:
cost:
xml:
attribute: true
days:
xml:
attribute: true
offers:
type: array
path: offers/offer
fields:
id:
xml:
attribute: true
available:
xml:
attribute: true
type:
xml:
attribute: true
param:
path: param
url: default
price: default
currencyId: default
categoryId: default
output:
{
"name" : "company name",
"company" : "Microsoft",
"url" : "https://microsoft.com",
"platform" : "woefjwoefi",
"agency" : "Agent",
"email" : "[email protected]",
"currencies" : [ {
"id" : "USD",
"rate" : 1
} ],
"categories" : [ {
"id" : 398,
"parentId" : 198,
"name" : "Lorum Ipsum"
}, {
"id" : 409,
"parentId" : null,
"name" : "Uncategorized"
} ],
"deliveryOptions" : null,
"offers" : [ {
"id" : "2016",
"available" : true,
"type" : "vendor.model",
"url" : "https://ms.com/id/1",
"price" : 35.0,
"currencyId" : "USD",
"categoryId" : "168",
}, {
"id" : "2017",
"available" : true,
"type" : "vendor.model",
"url" : "https://ms.com/id/2",
"price" : 250.0,
"currencyId" : "USD",
"categoryId" : "201",
} ]
}
This is the code I'm using to test it:
@Test
public void dsmParsing() throws IOException {
InputStream is = new FileInputStream("src/main/resources/dsm.yml");
DSMBuilder builder = new DSMBuilder(is);
DSM dsm = builder.setType(DSMBuilder.TYPE.XML).create(Shop.class);
Shop shop = (Shop) dsm.toObject(new File("src/main/resources/shop.xml"));
ObjectWriter ow = new ObjectMapper().writer().withDefaultPrettyPrinter();
System.out.println(ow.writeValueAsString(shop));
}
To answer your question about nested elements with attributes, you have to take into account that when you have this definition:
categories:
type: array
path: categories/category
when you define the fields
you will be describing the element <category>
.
In the example below, attributes id
and parentId
will be defined in the fields
segment as attributes, but to get the value of the actual category
element, you'll need to reference the current path with ./
. And if you had subelements, you just need to add those subelements in the fields
segment.
xml
<categories>
<category id="398" parentId="198">Lorum Ipsum</category>
</categories>
dsm
categories:
type: array
path: categories/category
fields:
name: ## you could call it 'value' or whatever you want
path: ./
id:
xml:
attribute: true
parentId:
xml:
attribute: true
This next example is quite interesting:
xml
<offers attr1="abc" attr2="def">
<offer id="2016" available="true">
<url>https://ms.com/id/1</url>
</offer>
</offers>
First, the you'll need another pojo to correctly represent offers
and its attributes:
Shop
public class Shop {
...
OffersList offers; // this attribute's name needs to match the one used in the dsm descriptor
}
New class OffersList
public class OffersList {
String attr1;
String attr2;
List<Offers> offer; // this attribute's name needs to match the one used in the dsm descriptor
}
Finally the dsm will look like:
offers: ## name should match with Shop's attribute 'offers' (OffersList)
type: object ## now offers is an object, is mandatory to define 'fields'
path: offers ## focus is on the <offers> element
fields:
attr1:
xml:
attribute: true
attr2:
xml:
attribute: true
offer: ## name should match with OffersList's attribute 'offer'
type: array
path: ../offers/offer ## will go back a level to indicate <offers> <offer> should be parsed as an array
fields:
id:
xml:
attribute: true
available:
xml:
attribute: true
url:
path: url
output
"offers" : {
"attr1" : "abc",
"attr2" : "def",
"offer" : [ {
"id" : "2016",
"available" : true,
"url" : "https://ms.com/id/1",
} ]
}
Upvotes: 1