Can you use both #XmlElement and #JsonProperty together inside a POJO class - java

I have a json payload and an xml payload, and I want to map both of the payloads into one POJO class. One endpoint returns a json and the other a xml. Can i combine both into one pojo class.
{
"house": 'big',
"has-large-house": "yes"
}
<completed-houses>
.....
</completed-houses>
public PayloadResponse(
#JsonProperty("house") final String house,
#JsonProperty("has-large-house") final String hasLargeHouseList,
#XmlElement(name="completed-houses") final String completeHouses) {
this.house = house;
this.hasLargeHouseList = hasLargeHouseList;
this.completeHouses = completeHouses;
}
and then getters and setter for these properties.

Yes! You can combine Jackson and JAXB annotations in the same POJO, using the Jackson module for JAXB annotations so that Jackson can understand JAXB annotations, and jackson-dataformat-xml for serializing to XML.
Here is an example:
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.dataformat.xml.XmlMapper;
import com.fasterxml.jackson.module.jaxb.JaxbAnnotationModule;
import lombok.Data;
import lombok.NoArgsConstructor;
import javax.xml.bind.annotation.XmlElement;
import java.io.IOException;
import java.io.StringWriter;
#Data
#NoArgsConstructor
public class PayloadResponse {
private String house;
#JsonProperty("has-large-house")
private boolean largeHouse;
#XmlElement(name = "completed-houses")
private String completedHouses;
public static void main(String[] args) throws IOException {
ObjectMapper xmlMapper = new XmlMapper();
JaxbAnnotationModule module = new JaxbAnnotationModule();
xmlMapper.registerModule(module);
PayloadResponse response = new PayloadResponse();
response.setHouse("The White House");
response.setLargeHouse(true);
response.setCompletedHouses("1600 Pennsylvania Ave.");
StringWriter stringWriter = new StringWriter();
// Serialize value as XML.
xmlMapper.writeValue(stringWriter, response);
System.out.println("XML=" + stringWriter);
// Serialize value as JSON.
ObjectMapper jsonMapper = new ObjectMapper();
stringWriter.getBuffer().setLength(0);
jsonMapper.writeValue(stringWriter, response);
System.out.println("JSON=" + stringWriter);
}
}
Outputs the following:
XML=<PayloadResponse>
<house>The White House</house>
<has-large-house>true</has-large-house>
<completed-houses>1600 Pennsylvania Ave.</completed-houses>
</PayloadResponse>
JSON={"house":"The White House",
"completedHouses":"1600 Pennsylvania Ave.",
"has-large-house":true}

Related

JaxbDto Serialization and deserialization

I need to receive some message with SOAP so I've generated a few classes by xsd-scheme and maven-jaxb2-plugin like this:
#XmlAccessorType(XmlAccessType.FIELD)
#XmlType(name = "Claim", propOrder = {
"field",
})
public class ClaimType {
#XmlElement(required = true, type = Integer.class, nillable = false)
protected Integer field;
public Integer getField() {
return bpType;
}
public void setField(Integer value) {
this.field= value;
}
}
After receiving message I need to send these to the next one microservice in wrap of HashMap.
I supposed to use ObjectMapper to convert:
//JAXB DTO --> JSON
ObjectMapper objectMapper = new ObjectMapper();
String jsonContent = objectMapper.writeValueAsString(claimType);
map.put("json", jsonContent);
//JSON --> JAXB DTO
ObjectMapper objectMapper = new ObjectMapper();
String json = map.get("json");
ClaimType claimType = objectMapper.readValue(json, ClaimType.class);
But the generated classes are haven't any constructors so I got the exception like "
No creator like default constructor are exists".
What is the best preactice to work with Jaxb Dto? Can I do smth to successful convert these json to object? Thanks in advance!
I've solved my problem by using ObjectMapper MixIn:
import javax.xml.bind.JAXBElement;
import javax.xml.namespace.QName;
#JsonIgnoreProperties(value = {"globalScope", "typeSubstituted", "nil"})
public abstract class JAXBElementMixIn<T> {
#JsonCreator
public JAXBElementMixIn(#JsonProperty("name") QName name,
#JsonProperty("declaredType") Class<T> declaredType,
#JsonProperty("scope") Class scope,
#JsonProperty("value") T value) {
}
}
And the convertation:
import com.fasterxml.jackson.databind.ObjectMapper;
ObjectMapper objectMapper = new ObjectMapper();
objectMapper.addMixIn(JAXBElement.class, JAXBElementMixIn.class);
solution link

How to convert a JsonNode instance to an actual pojo

At a certain point in my code, I have parse a JSON document, represented as a string, to a JsonNode, because I don't know yet the actual target pojo class type.
Now, some time later, I know the Class instance of the pojo and I want to convert this JsonNode to an actual pojo of that class (which is annotated with the proper #JsonProperty annotations). Can this be done? If so, how?
I am working with Jackson 2.10.x.
In this case you can use two methods:
treeToValue
convertValue
See below example:
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import java.io.File;
import java.util.StringJoiner;
public class JsonNodeConvertApp {
public static void main(String[] args) throws Exception {
File jsonFile = new File("./resource/test.json").getAbsoluteFile();
ObjectMapper mapper = new ObjectMapper();
JsonNode node = mapper.readTree(jsonFile);
System.out.println(mapper.treeToValue(node, Message.class));
System.out.println(mapper.convertValue(node, Message.class));
}
}
class Message {
private int id;
private String body;
// getters, setters, toString
}
Above code for JSON payload like below:
{
"id": 1,
"body": "message body"
}
prints:
Message[id=1, body='message body']
Message[id=1, body='message body']

Jackson deserialize child JSON to parent attribute

This is my requirement. I have below POJO.
class Car {
private String brandName;
private String color;
private Model model;
}
class Model {
private String modelName;
private String year;
}
If I get input json like below, then it should be de-serialized and mapped to both classes.
String json = "{\"brandName\" : \"Toyoto\", \"color\" : \"Silver\", \"model\" : {\"modelName\": \"Corolla\", \"year\": \"2019\"}}"
ObjectMapper mapper = new ObjectMapper();
Car car = mapper.readValue(json, Car.class);
assertEquals("Corolla", car.getModel().getModelName());
This case is fine.
But if I pass child json, that also should work without changing mapping class.
String json = "{\"modelName\": \"Corolla\", \"year\": \"2019\"}"
ObjectMapper mapper = new ObjectMapper();
Car car = mapper.readValue(json, Car.class);
assertEquals("Corolla", car.getModel().getModelName());
Any idea to solve this problem
You can implement your custom deserializer for Car class like:
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.DeserializationContext;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.deser.std.StdDeserializer;
import java.io.IOException;
/**
* #author Ehsan Zaery Moghaddam
*/
public class CarDeserializer extends StdDeserializer<Car> {
public CarDeserializer() {
this(null);
}
public CarDeserializer(Class<?> vc) {
super(vc);
}
#Override
public Car deserialize(JsonParser jp, DeserializationContext dctx)
throws IOException, JsonProcessingException {
JsonNode node = jp.getCodec().readTree(jp);
Car c = new Car();
Model cModel = new Model();
if(node.has("brandName")) {
// the JSON string contains both car and model details
c.setBrandName(node.get("brandName").asText());
c.setColor(node.get("color").asText());
JsonNode modelNode = node.get("model");
cModel.setModelName(modelNode.get("modelName").asText());
cModel.setYear(modelNode.get("year").asText());
} else {
// the JSON string just has model details
cModel.setModelName(node.get("modelName").asText());
cModel.setYear(node.get("year").asText());
}
c.setModel(cModel);
return c;
}
}
and when you're going to call the Jackson API to do the actual deserialization, register your deserializer in advance:
String json = "{\"modelName\": \"Corolla\", \"year\": \"2019\"}";
ObjectMapper mapper = new ObjectMapper();
SimpleModule module = new SimpleModule();
module.addDeserializer(Car.class, new CarDeserializer());
mapper.registerModule(module);
Car car = mapper.readValue(json, Car.class);
assertEquals("Corolla", car.getModel().getModelName());
This doesn't require to alter your POJOs. However, if you could do that, you had an option to register your custom deseriallizers using an annotation in your POJO class as below:
#JsonDeserialize(using = CarDeserializer.class)
public class Car { ... }

How do I ignore the root node when reading in a json rest api?

I am reading in a rest api that returns a Json file. I need to ignore the "result" and "optionChain" nodes. I am using Spring Boot with Jackson to deal with the mapping of objects.
Thanks in Advance!
For the Json File click here
Here is my Main:
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.http.*;
import org.springframework.web.client.RestTemplate;
import java.util.Collections;
#SpringBootApplication
public class OptionsImpliedMovementApplication implements CommandLineRunner {
public static void main(String[] args) {
SpringApplication.run(OptionsImpliedMovementApplication.class, args);
}
#Override
public void run(String... args) throws Exception {
RestTemplate restTemplate = new RestTemplate();
HttpHeaders headers = new HttpHeaders();
headers.setAccept(Collections.singletonList(MediaType.APPLICATION_JSON));
String resourceURL = url;
HttpEntity<String> entity = new HttpEntity<>(headers);
ResponseEntity<String> response = restTemplate.exchange(resourceURL, HttpMethod.GET,entity, String.class);
String rawJson = response.getBody();
ObjectMapper mapper = new ObjectMapper();
//need to read in json ignoring root node
}
Since you're already getting JSON response , then would recommend to use
restTemplate.exchange(URL, HttpMethod.GET, requestEntity, MyPOJO.class);
instead of String.class define your own POJO based on JSON response coming as you have attached in file.json.
In a handy way you can generate your POJO against your JSON from : http://www.jsonschema2pojo.org/ quickly and easily.
So it should look like :
#JsonInclude(JsonInclude.Include.NON_NULL)
#JsonPropertyOrder({
"optionChain"
})
public class MyPOJO {
#JsonProperty("optionChain")
private OptionChain optionChain;
// getters and setters
}
And another one :
#JsonInclude(JsonInclude.Include.NON_NULL)
#JsonPropertyOrder({
"result",
"error"
})
public class OptionChain {
#JsonProperty("result")
private List<Result> result = null;
#JsonProperty("error")
private Object error;
// getter and setters
}
And other like :
#JsonInclude(JsonInclude.Include.NON_NULL)
#JsonPropertyOrder({
"expirationDate",
"hasMiniOptions",
"calls",
"puts"
})
public class Option {
#JsonProperty("expirationDate")
private Integer expirationDate;
#JsonProperty("hasMiniOptions")
private Boolean hasMiniOptions;
#JsonProperty("calls")
private List<Call> calls = null;
#JsonProperty("puts")
private List<Put> puts = null;
So once you'll have the response as :
ResponseEntity<MyPOJO> response = restTemplate.exchange(resourceURL, HttpMethod.GET,entity, MyPOJO.class);
Then response.getBody will give the content inside optionChain node which is what you're looking for. You can then normally drill down to whatever node you want as now you have everything in plain java objects and you can ignore whatever you want or use whatever is needed.
Using objectMapper also you can then achieve the same :
ObjectMapper mapper = new ObjectMapper();
MyPojo myPojo= mapper.readValue(rawJson, MyPojo.class);
Quick (performant) and working.
final ObjectMapper objectMapper = new ObjectMapper();
final JsonNode jsonNode = objectMapper.readTree(rawJson);
final JsonNode result = jsonNode.get("optionChain")
.get("result");
final JsonNode firstResult = result.get(0);
final YourResultClass resultObject = objectMapper.treeToValue(firstResult, YourResultClass.class);
If you need to ignore unknown fields
final ObjectMapper objectMapper =
new ObjectMapper().configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);

JSON Formatting with Jersey, Jackson, & json.org/java Parser using Curl Command

Using Java 6, Tomcat 7, Jersey 1.15, Jackson 2.0.6 (from FasterXml maven repo), & www.json.org parser, I am trying to
pretty print the JSON String so it will look indented by the curl -X GET command line.
I created a simple web service which has the following architecture:
My POJOs (model classes):
Family.java
import javax.xml.bind.annotation.XmlRootElement;
#XmlRootElement
public class Family {
private String father;
private String mother;
private List<Children> children;
// Getter & Setters
}
Children.java
import javax.xml.bind.annotation.XmlRootElement;
#XmlRootElement
public class Children {
private String name;
private String age;
private String gender;
// Getters & Setters
}
Using a Utility Class, I decided to hard code the POJOs as follows:
public class FamilyUtil {
public static Family getFamily() {
Family family = new Family();
family.setFather("Joe");
family.setMother("Jennifer");
Children child = new Children();
child.setName("Jimmy");
child.setAge("12");
child.setGender("male");
List<Children> children = new ArrayList<Children>();
children.add(child);
family.setChildren(children);
return family;
}
}
My web service:
import java.io.IOException;
import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;
import javax.ws.rs.core.MediaType;
import org.codehaus.jackson.JsonGenerationException;
import org.codehaus.jackson.map.JsonMappingException;
import org.codehaus.jackson.map.ObjectMapper;
import org.codehaus.jettison.json.JSONException;
import org.json.JSONObject;
import org.json.JSONTokener;
import com.myapp.controller.myappController;
import com.myapp.resource.output.HostingSegmentOutput;
import com.myapp.util.FamilyUtil;
#Path("")
public class MyWebService {
#GET
#Produces(MediaType.APPLICATION_JSON)
public static String getFamily() throws IOException,
JsonGenerationException,
JsonMappingException,
JSONException,
org.json.JSONException {
ObjectMapper mapper = new ObjectMapper();
String uglyJsonString = mapper.writeValueAsString(FamilyUtil.getFamily());
System.out.println(uglyJsonString);
JSONTokener tokener = new JSONTokener(uglyJsonString);
JSONObject finalResult = new JSONObject(tokener);
return finalResult.toString(4);
}
}
When I run this using:
curl -X GET http://localhost:8080/mywebservice
I get this in my Eclipse's console:
{"father":"Joe","mother":"Jennifer","children":[{"name":"Jimmy","age":"12","gender":"male"}]}
But from the curl command on the command line (this response is more important):
"{\n \"mother\": \"Jennifer\",\n \"children\": [{\n \"age\": \"12\",\n \"name\": \"Jimmy\",\n \"gender\": \"male\"\n }],\n \"father\": \"Joe\"\n}"
This is adding newline escape sequences and placing double quotes (but not indenting like it should it does have 4 spaces after the new line but its all in one line).
Would appreciate it if someone could point me in the right direction.
What I believe is happening is your currently configured message body reader is taking your String returned from your method, and escaping it properly so that it is a valid JSON string (since json doesn't let newlines inside of string constants).
Here's what you do... I'm assuming that you are using Jackson's included Message Body Writers. (e.g. JacksonJsonProvider)
You create a #Provider that sets up an ObjectMapper instance with Pretty Printing enabled like so:
#Provider
public class JacksonObjectMapperProvider implements ContextResolver<ObjectMapper> {
/**
* {#inheritDoc}
*/
#Override
public ObjectMapper getContext(final Class<?> type) {
final ObjectMapper toReturn = new ObjectMapper();
toReturn.enable(SerializationFeature.INDENT_OUTPUT); // This is the important setting
toReturn.disable(MapperFeature.USE_ANNOTATIONS); // I have this one on but it's probably for other resources in the container testing it in, I don't know if you'd need it.
return toReturn;
}
}
You then have your Resource return the resolved Family object instead of trying to transform it to Json... let the Message Body Writer do that... i.e.
public class MyWebService {
#GET
#Produces(MediaType.APPLICATION_JSON)
public Family getFamily()
return FamilyUtil.getFamily()
}
}
and Voila:
$ curl http://<server>/<ctx-root>/<path>
{
"father" : "Joe",
"mother" : "Jennifer",
"children" : [ {
"name" : "Jimmy",
"age" : "12",
"gender" : "male"
} ]
}
Now I glossed over getting the Provider and MessageBodyReader registered with your JAX-RS Application configuration, but that could vary greatly depending upon if you're using Jersey's servlet, using a custom Application, using Guice, or any number of other ways of setting up your JAX-RS stack.
Just use GSON library
This is an example for Jersey
normal Gson:
Gson gson = new Gson();
String json = gson.toJson(obj);
System.out.println(json);
the JSON output is display as compact mode like following :
{"data1":100,"data2":"hello","list":["String 1","String 2","String 3"]}
To enable pretty print, you should use GsonBuilder return a Gson object :
Gson gson = new GsonBuilder().setPrettyPrinting().create();
String json = gson.toJson(obj);
System.out.println(json);
Output will like this:
{
"data1": 100,
"data2": "hello",
"list": [
"String 1",
"String 2",
"String 3"
]
}
Full example from mkyong
package com.mkyong.core;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
public class GsonExample {
public static void main(String[] args) {
DataObject obj = new DataObject();
// Gson gson = new Gson();
Gson gson = new GsonBuilder().setPrettyPrinting().create();
String json = gson.toJson(obj);
System.out.println(json);
}
}

Categories

Resources