Convert XML key value mapping to JSON object - java

I currently have an object which is a key-value pair that I have converted from XSD to POJO using JAXB and I tried using Jackson 2.x to get the JSON output for the POJO. This JSON output looks like:
[ {
"key" : "key1",
"value" : 1
}, {
"key" : "key2",
"value" : "2"
}, {
"key" : "key3",
"value" : [ ]
} ]
Currently my XSD generated POJO looks like:
#XmlAccessorType(XmlAccessType.FIELD)
#XmlType(name = "MapEntryType", propOrder = {
"value"
})
public class MapEntryType {
#XmlElement(required = true)
protected Object value;
#XmlAttribute(name = "key", required = true)
protected String key;
}
#XmlAccessorType(XmlAccessType.FIELD)
#XmlType(name = "", propOrder = {
"mapEntries"
})
#XmlRootElement(name = "EventsSearchResponse")
public class EventsSearchResponse {
#XmlElement(name = "MapEntry")
protected List<MapEntryType> mapEntries;
}
I would like to generate the map as a simple JSON object:
{ "key1" : 1, "key2" : "2", "key3" : []}
I went over the annotations that are available in Jackson from http://wiki.fasterxml.com/JacksonAnnotations but I have not been able to find a way to perform this type of conversion. Any help regarding this would be really appreciated! Thanks.

I was able to achieve the desired results with a custom serializer (written as inner class for convinience):
public static class EventsSearchResponseSerializer extends JsonSerializer<EventsSearchResponse>
{
#Override
public void serialize(EventsSearchResponse res, JsonGenerator gen, SerializerProvider serializers)
throws IOException, JsonProcessingException
{
gen.writeStartObject();
for (MapEntryType t : res.mapEntries) {
gen.writeObjectField(t.key, t.value);
}
gen.writeEndObject();
}
}
added the proper annotation to the POJO:
#JsonSerialize(using = EventsSearchResponseSerializer.class)
#XmlRootElement(name = "EventsSearchResponse")
public static class EventsSearchResponse {
#XmlElement(name = "MapEntry")
public List<MapEntryType> mapEntries;
}
calling the Jackson mapper:
public static void main(String[] args)
{
EventsSearchResponse r = new EventsSearchResponse();
r.mapEntries = new ArrayList<>();
MapEntryType t = new MapEntryType();
t.key = "key1";
t.value = new Integer(1);
r.mapEntries.add(t);
t = new MapEntryType();
t.key = "key2";
t.value = new Integer(2);
r.mapEntries.add(t);
t = new MapEntryType();
t.key = "key2";
t.value = new String[0];
r.mapEntries.add(t);
try {
System.out.println(new ObjectMapper().writeValueAsString(r));
} catch (Exception e) {
e.printStackTrace();
}
}
gives result:
{"key1":1,"key2":2,"key2":[]}

Related

Jackson deserialize JSON into pojo with map property

Can somebody help me, how I can deserialize the following JSON, which I can not change?
I am using Jackson for serialization.
{
"columns": [
{
"header": "Heading1",
},
{
"header": "Heading2",
}
],
"rows": [
"id": 1,
"Heading1": {
"value": "Value1"
},
"Heading2": {
"value": "Value2"
}
]
}
Columns can have unknown number of headers and their value eg. "Header1" is used in the rows array.
So far I have the following structure:
public class QueryResult {
private ColumnConfig[] columns;
private QueryResultRow[] rows;
}
public class ColumnConfig {
private String header;
}
public class QueryResultRow {
private int id;
private Map<String, CellValue> values;
}
public class CellValue{
private String value;
}
The problem is that the Map is empty when I deserialize into QueryResult;
I read about TypeReference but I do not know how I can specify a TypeReference<HashMap<String,CellValue>> for the property values in QueryResultRow.
Edit:
My ObjectMapper code is the following:
ObjectMapper mapper = new ObjectMapper();
mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
String queryResultString = loadQuery(queryPath);
QueryResult result = mapper.readValue(queryResultString, QueryResult.class);
The content of queryResultString is the JSON above.
First problem is your JSON is invalid. I assume it should be something like this,
{
"columns": [
{
"header": "Heading1"
},
{
"header": "Heading2"
}
],
"rows": [
{
"id": 1,
"Heading1": {
"value": "Value1"
},
"Heading2": {
"value": "Value2"
}
}
]
}
Then answer is quite straightforward. You need to change your QueryResultRow as follows,
class QueryResultRow {
private int id;
private Map<String, CellValue> values = new HashMap<>();
#JsonAnySetter
public void addValues(String k, CellValue v) {
values.put(k, v);
}
}
Then I think you should good to go.
Here is a complete working example,
public class Main {
public static void main(String[] args) throws IOException {
String s = "{\"columns\":[{\"header\":\"Heading1\"},{\"header\":\"Heading2\"}],\"rows\":[{\"id\":1,\"Heading1\":{\"value\":\"Value1\"},\"Heading2\":{\"value\":\"Value2\"}}]}";
ObjectMapper om = new ObjectMapper();
QueryResult queryResult = om.readValue(s, QueryResult.class);
System.out.println(queryResult);
}
}
#Getter
#Setter
#ToString
class QueryResult {
private ColumnConfig[] columns;
private QueryResultRow[] rows;
}
#Getter
#Setter
#ToString
class ColumnConfig {
private String header;
}
#Getter
#Setter
#ToString
class QueryResultRow {
private int id;
private Map<String, CellValue> values = new HashMap<>();
#JsonAnySetter
public void addValues(String k, CellValue v) {
values.put(k, v);
}
}
#Getter
#Setter
#ToString
class CellValue{
private String value;
}

Recommendation for resetting data in json object using Java

I need a recommendation for this situation.
I have a json object in string format that will have pattern like this:
{
"productCard" : {
"productA" : {
"state" : "Y",
"desc" : "AAA",
"someProp" : 112
},
"productB" : {
"state" : "X",
"desc" : " BBB ",
"listSomeThing" : [
{
"p1" : 1,
"p2" : "2"
},
{
"p2" : "3"
}
]
}
// PRODUCT CAN ADD MORE IN FUTRE
// ALSO CAN HAVE OTHER OBJECT TYPE
}
// THIS CAN HAVE OTHER OBJECT THAT MAY BE NON RELATE INFORMATION WITH PRODUCT CARD
}
and then this will be parsed to an object like this:
class Product {
protected String state
protected String desc
}
class SomeThing {
private int p1
private String p2
}
class ProductA extend Product {
private int someProp
}
class ProductB extend Product {
private List<SomeThing> listSomeThing
}
class ProductCard {
private ProductA prodctA
private ProductB productB
}
class BaseObject {
private ProductCard productCard
}
If I need to reset some field value in each product, and then parse to string format again, should I:
(1) create a new function in Product and then override in some child class for extra method:
class Product {
void reset(){
this.state = "X"
this.desc = ""
}
}
class productB extend Product {
#override
void reset(){
super.reset()
this.listSomeThing = new ArrayList<>()
}
}
and in base object create new function:
class ProductCard {
private ProductA productA
private ProductB productB
void resetAllProduct(){
this.productA.reset()
this.productB.reset()
}
}
class BaseObject {
private ProductCard productCard
void resetAllProductCard(){
this.productCard.resetAllProduct()
}
}
then call BaseObject.resetAllProductCard() where business needs to reset?
(2) create new function in business class? Or some util class:
void reset(ProdctCard productCard){
ProductA productA = productCard.getProductA();
productA.setState("X")
productA.setDesc("")
ProductB productB = productCard.getProdctB();
productB.setState("X")
productB.setDesc("")
productB.setListSomeThing(new ArrayList<>())
}
(3) another approach?
I would use Jackson Project for that job:
public String reset(String json) throws IOException {
ObjectMapper mapper = new ObjectMapper();
JsonNode jsonNode = mapper.readTree(json);
JsonNode productCardNode = jsonNode.get("productCard");
productCardNode.forEach(node -> ((ObjectNode) node).put("state", "X").put("desc", ""));
ObjectNode productBNode = (ObjectNode) productCardNode.get("productB");
productBNode.putArray("listSomeThing");
return jsonNode.toPrettyString();
}
Then:
String jsonReseted = reset(json);
System.out.println(jsonReseted);
Output:
{
"productCard" : {
"productA" : {
"state" : "X",
"desc" : "",
"someProp" : 112
},
"productB" : {
"state" : "X",
"desc" : "",
"listSomeThing" : [ ]
}
}
}

JsonView serialization that depends on some condition/property name etc

One of my classes has 3 properties of same type. Now I'm trying to serialize it do JSON, but one of those properties needs to be serialized differently - basically one of those properties is "internal" and I need only id of it, the rest of them must be fully serialized.
What I came so far:
#NoArgsConstructor #AllArgsConstructor #Data
public static class Id {
#JsonView(View.IdOnly.class) private long id;
}
#NoArgsConstructor #AllArgsConstructor #Data
public static class Company extends Id {
#JsonView(View.Tx.class) private String name;
#JsonView(View.Tx.class) private String address;
}
#NoArgsConstructor #AllArgsConstructor #Data
public static class Transaction {
#JsonView(View.Tx.class) private Company from;
#JsonView(View.Tx.class) private Company to;
#JsonView(View.IdOnly.class) private Company createdBy;
}
public static class View {
public interface Tx extends IdOnly {}
public interface IdOnly {}
}
And quick test for it:
#Test
void test() throws JsonProcessingException {
Company s = new Company("Source", "address_from");
Company d = new Company("Destination", "address_to");
final Transaction t = new Transaction(s, d, s);
final ObjectMapper m = new ObjectMapper();
System.out.println(m.writerWithDefaultPrettyPrinter().withView(View.Tx.class).writeValueAsString(t));
}
And output is:
{
"from" : {
"id" : 0,
"name" : "Source",
"address" : "address_from"
},
"to" : {
"id" : 0,
"name" : "Destination",
"address" : "address_to"
},
"createdBy" : {
"id" : 0,
"name" : "Source",
"address" : "address_from"
}
}
Now, question, how can I customize serialization of createBy property? I need following output:
{
"from" : {
"id" : 0,
"name" : "Source",
"address" : "address_from"
},
"to" : {
"id" : 0,
"name" : "Destination",
"address" : "address_to"
},
"createdBy" : {
"id" : 0,
}
}
Oh, I think that answer for that is very simple:
Mark createdBy field with #JsonSerialize(using = CS.class)
Implement custom serializer as follows:
public static class CS extends JsonSerializer<Company> {
#Override
public void serialize(Company company, JsonGenerator jgen, SerializerProvider serializerProvider) throws IOException {
jgen.writeStartObject();
jgen.writeNumberField("id", company.getId());
jgen.writeEndObject();
}
}

Avoiding type info for the list after serialization in Jackson

I have a class in C# like below,
class Person
{
List<String> hobbies;
}
When I convert it to Java class using Jaxb it looks like
class Person
{
#XmlElement(name = "hobbies")
ArrayOfString hobbies;
}
#XmlAccessorType(XmlAccessType.FIELD)
#XmlType(name = "ArrayOfString", propOrder = {
"string"
})
public class ArrayOfString {
#XmlElement(nillable = true)
protected List<String> string;
}
All good till now, but now when I am trying to convert this object to Json using Jackson Objectmapper like so,
ObjectMapper mapper = new ObjectMapper();
ObjectWriter ow = mapper.writer().withDefaultPrettyPrinter();
String jsonString= ow.writeValueAsString(Person);
I get the json output as below,
{
"hobbies" : { "string" : [ "reading","writing"] }
}
Now I need the json to look like below with no string keyword.
{
"hobbies" : [ "reading","writing"]
}
Unfortunately I cannot change the c# generated xsd.
Any solutions?
Not sure about your JAXB part, but if you define your bean classes as:
class Person
{
private ArrayOfString hobbies;
public ArrayOfString getHobbies() {
return hobbies;
}
public void setHobbies(ArrayOfString hobbies) {
this.hobbies = hobbies;
}
}
class ArrayOfString {
#JsonValue
protected List<String> string;
public List<String> getString() {
return string;
}
public void setString(List<String> string) {
this.string = string;
}
}
Note the annotation #JsonValue on the field protected List<String> string.
Now you will get the expected result.
public static void main(String[] args) throws JsonProcessingException {
Person person = new Person();
person.hobbies = new ArrayOfString();
person.hobbies.string = Arrays.asList("reading","writing");
ObjectMapper mapper = new ObjectMapper();
ObjectWriter ow = mapper.writer().withDefaultPrettyPrinter();
String jsonString= ow.writeValueAsString(person);
System.out.println(jsonString);
}
Output
{
"hobbies" : [ "reading", "writing" ]
}
Note: You can still add your JAXB annotations on the beans with no side effect.

Order of JSON objects using Jackson's ObjectMapper

I'm using ObjectMapper to do my java-json mapping.
ObjectWriter ow = new ObjectMapper().writer().withDefaultPrettyPrinter();
ow.writeValue(new File( fileName +".json"), jsonObj);
this is my java class:
public class Relation {
private String id;
private String source;
private String target;
private String label;
private List<RelAttribute> attributes;
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getSource() {
return source;
}
public void setSource(String source) {
this.source = source;
}
public String getTarget() {
return target;
}
public void setTarget(String target) {
this.target = target;
}
public String getLabel() {
return label;
}
public void setLabel(String label) {
this.label = label;
}
public void setAttributes(List<RelAttribute> attributes) {
this.attributes = attributes;
}
public List<RelAttribute> getAttributes() {
return attributes;
}
}
this is what I get:
{
"id" : "-75da69d3-79c8-4000-a3d8-b10350a57a7e",
"attributes" : [ {
"attrName" : "ID",
"attrValue" : ""
}, {
"attrName" : "Description",
"attrValue" : "Primary Actor"
}, {
"attrName" : "Status",
"attrValue" : ""
} ],
"label" : "new Label",
"target" : "-46b238ac-b8b3-4230-b32c-be9707f8b691",
"source" : "-daa34638-061a-45e0-9f2e-35afd6c271e0"
}
So my question now is, how can I get this json output:
{
"id" : "-75da69d3-79c8-4000-a3d8-b10350a57a7e",
"label" : "new Label",
"target" : "-46b238ac-b8b3-4230-b32c-be9707f8b691",
"source" : "-daa34638-061a-45e0-9f2e-35afd6c271e0",
"attributes" : [ {
"attrName" : "ID",
"attrValue" : ""
}, {
"attrName" : "Description",
"attrValue" : "Primary Actor"
}, {
"attrName" : "Status",
"attrValue" : ""
} ]
}
I want it with same order as in my java declaration. Is there a way to specify it ? Maybe with annotations or stuff like that ?
#JsonPropertyOrder({ "id", "label", "target", "source", "attributes" })
public class Relation { ... }
Do you know there is a convenient way to specify alphabetic ordering?
#JsonPropertyOrder(alphabetic = true)
public class Relation { ... }
If you have specific requirements, here how you configure custom ordering:
#JsonPropertyOrder({ "id", "label", "target", "source", "attributes" })
public class Relation { ... }
The ordering of fields within a generated .class is indeterminate, so you can't count on that.
If you want specific ordering per class then you'll need to use the one of the approaches specified in other answers.
If you want everything to default to alphabetical ordering (e.g. for consistency in how the JSON is structured) then you can configure the ObjectMapper like this:
ObjectMapper mapper = new ObjectMapper();
mapper.setConfig(mapper.getSerializationConfig()
.with(MapperFeature.SORT_PROPERTIES_ALPHABETICALLY));
For more consistent JSON consider also adding:
.with(SerializationFeature.ORDER_MAP_ENTRIES_BY_KEYS)
One advantage of this approach is that you don't have to modify each class being serialized.
I discovered a third way today in case alphabetic is not your desired sorting order. It turns out adding a #JsonProperty annotation on a field places it last when writing. I discovered that when I wanted to specify a property name which did not conform to java naming conventions.
By Adding an index attribute you can define the order. Lowest index is placed first.
#JsonProperty(index=20)
String prop1;
#JsonProperty(index=10)
String prop2;
Would render:
{"prop2": "valueProp2", "prop1": "valueProp1"}
You can use #XmlAccessorType(XmlAccessType.FIELD)
#XmlType(name = "response", propOrder = { "prop1", "prop2",
"prop3", "prop4", "prop5", "prop6" }).
#JsonPropertyOrder requires a new jar to be added.
As per this documentation, you can configure Jackson2ObjectMapperBuilder globally. This class is available in spring-web dependency.
#Bean
public Jackson2ObjectMapperBuilder objectMapperBuilder() {
Jackson2ObjectMapperBuilder builder = new Jackson2ObjectMapperBuilder();
builder.featuresToEnable(MapperFeature.SORT_PROPERTIES_ALPHABETICALLY);
return builder;
}
Also, you can use #JsonProperty(index) to determine the order in inherited classes as well.
class animal {
#JsonProperty(index=2)
int p1;
#JsonProperty(index=3)
int p2;
}
class cat extends animal{
#JsonProperty(index=1)
int p3;
}

Categories