GSON: Convert JSON To Object with ignorance - java

I had JSON model for success some this
{
data: {
.....
}
}
so I've create model:
Class Model{
public Data data;
}
and I've used
new Gson().fromJson(Model.class) to convert in Object. It works fine. Now my problem starts with when I get error in same API and its json Get changed
{
message: {
.....
}
}
I want to use same model to be serialized in Json.
Class Model{
public Data data;
public Massage message;
}
But it gives me following Exception: Expected a string but was BEGIN_OBJECT at line 1 column 13

Just configure and use ObjectMapper from Jackson lib:
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.8.3</version>
</dependency>
The mapper should be defined as
ObjectMapper mapper = new ObjectMapper().configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
Please see full Demo example
public class Demo {
public static void main(String[] args) throws IOException {
String jsonData = "{" +
" \"data\": {" +
" }" +
"}";
String jsonMessage = "{" +
" \"message\": {" +
" }" +
"}";
ObjectMapper mapper = new ObjectMapper().configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
Model modelData = mapper.readValue(jsonData, Model.class);
Model modelMessage = mapper.readValue(jsonMessage, Model.class);
System.out.println(modelData);
System.out.println(modelMessage);
}
}
class Model {
private Data data;
private Message message;
#Override
public String toString() {
return "Model{" +
"data=" + data +
", message=" + message +
'}';
}
public Data getData() {
return data;
}
public void setData(Data data) {
this.data = data;
}
public Message getMessage() {
return message;
}
public void setMessage(Message message) {
this.message = message;
}
private static class Data {}
private static class Message {}
}
BTW, ObjectMapper is Thread Safe, so you can define one constant to not create it each time when you read Json.
More info you can find here: http://fasterxml.github.io/jackson-databind/javadoc/2.8/com/fasterxml/jackson/databind/ObjectMapper.html

Related

Jackson should not ignore root element tag in multi root xml content

I have the following xml content which I want to deserialize to JSON,
Input Content
<TransmissionAck>
<EchoedTransmissionHeader>
<TransmissionHeader>
<ReferenceTransmissionNo>26218</ReferenceTransmissionNo>
</TransmissionHeader>
</EchoedTransmissionHeader>
</TransmissionAck>
Expected output
{
"TransmissionAck": {
"EchoedTransmissionHeader":{
"TransmissionHeader":{
"ReferenceTransmissionNo":"26177"
}
}
}
}
Actual Output
{
"EchoedTransmissionHeader":{
"EchoedTransmissionHeader":{
"TransmissionHeader":{
"ReferenceTransmissionNo":"26177"
}
}}}
I am passing dynamic content from RestController(Spring Boot)
Mono<ResponseEntity<String>> otmXmlResponse = webRequestsService
.handlePost(Files.readString(Paths.get(outputFile.getAbsolutePath()), StandardCharsets.US_ASCII));
String body = otmXmlResponse.block().getBody();
String r = testXmlResponse(body);
============
public <T> T testXmlResponse(String xml) throws JsonMappingException, JsonProcessingException {
ObjectMapper objectMapper = new ObjectMapper();
XmlMapper xmlMapper = new XmlMapper();
xmlMapper.registerModule(new SimpleModule().addDeserializer(JsonNode.class,
new JsonNodeDeserializer() {
private static final long serialVersionUID = -5947022035338738709L;
public JsonNode deserialize(JsonParser p, DeserializationContext ctxt)
throws IOException {
XMLStreamReader xmlP = ((FromXmlParser) p).getStaxReader();
String rootName = xmlP.getLocalName().toString();
return ctxt.getNodeFactory().objectNode().set(rootName, super.deserialize(p, ctxt));
}
}));
JsonNode entries = xmlMapper.readTree(inputFile);
String json = objectMapper.writerWithDefaultPrettyPrinter().writeValueAsString(entries);
return (T) json;
}
When I run a unit test I get expected output, but when I call the deserialize method as above with dynamic content from controller, the sub-root element is taken as root. I cannot use a POJO for mapping. Any idea how I can get the expected output using Jackson library? Thanks.
Try this, I used
<dependency>
<groupId>org.json</groupId>
<artifactId>json</artifactId>
<version>20140107</version>
</dependency>
import org.json.*;
public class ConvertXML {
public static String xml = "<TransmissionAck>\r\n" + " <EchoedTransmissionHeader>\r\n"
+ " <TransmissionHeader>\r\n"
+ " <ReferenceTransmissionNo>26218</ReferenceTransmissionNo>\r\n"
+ " </TransmissionHeader>\r\n" + " </EchoedTransmissionHeader>\r\n" + "</TransmissionAck>";
public static void main(String[] args) {
try {
JSONObject json = XML.toJSONObject(xml);
String jsonString = json.toString(4);
System.out.println(jsonString);
} catch (JSONException e) {
System.out.println(e.toString());
}
}
}

Java/Jackson Annotations: Mapping either an array or object?

If I can get either of the following JSON bodies as responses:
{
"error" : "some error",
"code": 123
}
or
[
{
"name" : "name",
"value" : "value"
},
{
"name" : "name",
"value" : "value"
}
]
Is there a way to map either of these response to below Java POJO using annotations ?
public class Response {
String error;
int code;
List<NameValuePair> nameValuePairs;
}
Out of the box, only the error response will deserialise. The issue is they are fundamentally different types - an object vs an array.
You can make it work with a custom module for jackson, as described here
One possible response is a JSON Object and other is a JSON Array. In that case it is not possible to create one POJO class to handle it. Also, these two payloads means to different things: one is SUCCESS and other is ERROR payloads. In that case I would use Facade design pattern to create extra layer and hide this complex logic there. It could look like below:
class ResponseDeserialiserFacade {
private final ObjectMapper mapper = new ObjectMapper();
public List<NameValuePair> deserialisePairs(String json) {
try {
return mapper.readValue(json, new TypeReference<List<NameValuePair>>() {
});
} catch (IOException e) {
try {
Error error = mapper.readValue(json, Error.class);
throw new RequestApiException(error, e);
} catch (IOException e1) {
throw new RequestApiException(Error.from("Can not parse: " + json), e1);
}
}
}
}
As you noticed I introduced new exception:
class RequestApiException extends RuntimeException {
private final Error error;
RequestApiException(Error error, Exception base) {
super(base);
this.error = error;
}
public Error getError() {
return error;
}
}
with Error class:
class Error {
private String error;
private String code;
public static Error from(String message) {
Error e = new Error();
e.error = message;
return e;
}
// getters, setters, toString
}
Now we can test it for SUCCESS and ERROR payloads:
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;
import java.io.IOException;
import java.util.List;
public class JsonApp {
public static void main(String[] args) {
String success = "[\n" +
" {\n" +
" \"name\" : \"name\",\n" +
" \"value\" : \"value\"\n" +
" },\n" +
" {\n" +
" \"name\" : \"name\",\n" +
" \"value\" : \"value\"\n" +
" }\n" +
"]\n";
tryToParse(success);
String error = "{\n" +
" \"error\" : \"some error\",\n" +
" \"code\": 123\n" +
"}";
tryToParse(error);
}
private static void tryToParse(String json) {
ResponseDeserialiserFacade deserialiser = new ResponseDeserialiserFacade();
try {
List<NameValuePair> pairs = deserialiser.deserialisePairs(json);
System.out.println("SUCCESS: " + pairs);
} catch (RequestApiException e) {
System.out.println("ERROR: " + e.getError());
}
}
}
class NameValuePair {
private String name;
private String value;
// getters, setters, toString
}
Above code prints:
SUCCESS: [NameValuePair{name='name', value='value'}, NameValuePair{name='name', value='value'}]
ERROR: Error{error='some error', code='123'}
As you can see, we treated error message like as exception.

Extracting the value of an element from a JSON

I'm trying to make a test where I get some documents based on the id of the batch they belong to. More specifically, I want to check that a specific batchPublicId is in the response body. I am using okhttp for the test.
This a shorter version of the json:
{
"_embedded": {
"invoices": [
{
"type": "INVOICE",
"publicId": "27bc8426-17cf-4fe5-9278-64108ae05e4b",
"deliveryStatus": null,
"processingStatus": "INITIATED",
"batchPublicId": "0000000000000000000000001"
}
]
}
}
I'm new to json and this is how far I got with the problem:
String invoicesJsonData = response.body().string();
JSONObject invoicesJsonObject = new JSONObject(invoicesJsonData);
Assert.assertTrue(invoicesJsonObject.getJSONObject("_embedded") !=null && invoicesJsonObject.getJSONObject("_embedded").has("invoices"));
I would like to verify that batchPublicId has the value mentioned in the json. Is there a way to do this? Thank you.
String invoicesJsonData = response.body().string();
JSONObject invoicesJsonObject = new JSONObject(invoicesJsonData);
JSONObject invoicesJsonObject1 = invoicesJsonObject.getJSONObject("_embedded");
JSONArray f2=invoicesJsonObject1.getJSONArray("invoices");
for(int i=0;i<f2.length();i++){
JSONObject obj=f2.getJSONObject(i);
if(obj.get("batchPublicId")!=null){
System.out.println(obj.get("batchPublicId"));
}
You can do something like this,Which worked out for me sometimes back.
String invoicesJsonData = response.body().string();
JSONObject invoicesJsonObject = new JSONObject(invoicesJsonData);
JSONObject invoicesJsonObject = json.getJSONObject("invoicesJsonObject");
String batchPublicId = invoicesJsonObject.getString("batchPublicId");
System.out.println( "batchPublicId: " + batchPublicId );
if(batchPublicId !=null){
// do something
}
Not sure about the syntax.Giving you a hint.
you can check any keys is there in json object or not like below :
if(jsonObject1.has("batchPublicId")){
String batchPublicId = jsonObject1.optString("batchPublicId");
Log.i(getClass().getSimpleName(), "batchPublicId=" + batchPublicId);}
has method is used to find any key is there in jsonobject or not.
In my opinion, a better approach for this would be to create a POJO from this JSON string, and extract the information you need using simply the getters
For example:
Wrapper class:
#JsonIgnoreProperties(ignoreUnknown = true)
#JsonRootName(value = "_embedded")
public class Embeded {
#JsonProperty("invoices")
private List<Invoice> invoices;
public Embeded() {}
public List<Invoice> getInvoices() {
return invoices;
}
public void setInvoices(List<Invoice> invoices) {
this.invoices = invoices;
}
}
Invoice class:
#JsonIgnoreProperties(ignoreUnknown = true)
public class Invoice {
#JsonProperty("type")
private String type;
#JsonProperty("publicId")
private String publicId;
#JsonProperty("deliveryStatus")
private String deliveryStatus;
#JsonProperty("processingStatus")
private String processingStatus;
#JsonProperty("batchPublicId")
private String batchPublicId;
public Invoice() {}
public String getType() {
return type;
}
public void setType(String type) {
this.type = type;
}
public String getPublicId() {
return publicId;
}
public void setPublicId(String publicId) {
this.publicId = publicId;
}
public String getDeliveryStatus() {
return deliveryStatus;
}
public void setDeliveryStatus(String deliveryStatus) {
this.deliveryStatus = deliveryStatus;
}
public String getProcessingStatus() {
return processingStatus;
}
public void setProcessingStatus(String processingStatus) {
this.processingStatus = processingStatus;
}
public String getBatchPublicId() {
return batchPublicId;
}
public void setBatchPublicId(String batchPublicId) {
this.batchPublicId = batchPublicId;
}
}
Test:
public void json_test() throws JsonParseException, JsonMappingException, IOException {
String json = "{"
+ "\"_embedded\": {"
+ "\"invoices\": ["
+ "{"
+ "\"type\": \"INVOICE\","
+ "\"publicId\": \"27bc8426-17cf-4fe5-9278-64108ae05e4b\","
+ "\"deliveryStatus\": null,"
+ "\"processingStatus\": \"INITIATED\","
+ "\"batchPublicId\": \"0000000000000000000000001\""
+ "}"
+ "]"
+ "}"
+ "}";
ObjectMapper mapper = new ObjectMapper();
mapper.configure(Feature.UNWRAP_ROOT_VALUE, true);
List<Invoice> invoices = mapper.readValue(json, Embeded.class).getInvoices();
Assert.assertTrue(StringUtils.equals(invoices.get(0).getBatchPublicId(), "0000000000000000000000001"));
}
If I understand your right, you just need to call:
Assert.assertTrue(invoicesJsonObject.getString("batchPublicId").equals("0000000000000000000000001"));"
If you want to create a test for JSON Validation, you can use the JSONAssert.
JSONAsset give the method assertEquals, that compare two json structures, strict identic or not.
final String expected_result = YOUR_EXPECTED_RESULT;
JSONAssert.assertEquals(YOUR_EXPECTED_JSON_RESULT, RESULT_FROM_RESPONSE_BODY, false);
The last boolean parameter defines if you want an strict comparation or just compare if your expected result is in result from response.

Json to pojo convertions

How we convert following type of json into java object
{
"complaint_Map": {
"1000067730": "3011351597604397",
"1000067730-06": "10582576134561065"
}
}
if anyone have any idea about this tell how we do that.
With jackson
import com.fasterxml.jackson.databind.ObjectMapper;
Try
ObjectMapper mapper = new ObjectMapper();
String jsonInString = "{\"complaint_Map\":{\"1000067730\":\"3011351597604397\",\"1000067730-06\":\"10582576134561065\"}}";
Map<String,Object> pojo = mapper.readValue(jsonInString, Map.class);
System.out.println(((Map<String,Object>)pojo.get("complaint_Map")).get("1000067730")+"");
will print
3011351597604397
In java you can use Jackson library that converts a simple POJO to/from JSON.
From Wikipedia:
Jackson is a high-performance JSON processor for Java. Developers of it extol the combination of fast, correct, lightweight, and ergonomic attributes of the library.
Here an example taken by Wikipedia:
public class ReadWriteJackson {
public static void main(String[] args) throws IOException {
ObjectMapper mapper = new ObjectMapper();
String jsonInput =
"{\"id\":0,\"firstName\":\"Robin\",\"lastName\":\"Wilson\"}";
Person q = mapper.readValue(jsonInput, Person.class);
System.out.println("Read and parsed Person from JSON: " + q);
Person p = new Person("Roger", "Rabbit");
System.out.print("Person object " + p + " as JSON = ");
mapper.writeValue(System.out, p);
}
}
You can do it with ObjectMapper from jackson.
Suppose the json is defines a java object,
then it can be done by
import org.codehaus.jackson.map.ObjectMapper;
YourObject mappingClassObject = new YourObject();
ObjectMapper mapper = new ObjectMapper();
try{
mappingClassObject = mapper.readValue(yourJSON, YourObject.class);
}catch(Exception e){
e.printStackTrace();
}
If you want a solution using Jackson library, here it is.
The custom class:
#JsonRootName("complaint_Map")
public class Complaint {
private String firstKey;
private String secondKey;
#JsonProperty("1000067730")
public String getFirstKey() {
return firstKey;
}
#JsonProperty("1000067730")
public void setFirstKey(String firstKey) {
this.firstKey = firstKey;
}
#JsonProperty("1000067730-06")
public String getSecondKey() {
return secondKey;
}
#JsonProperty("1000067730-06")
public void setSecondKey(String secondKey) {
this.secondKey = secondKey;
}
#Override
public String toString() {
return "Complaint{" +
"firstKey='" + firstKey + '\'' +
", secondKey='" + secondKey + '\'' +
'}';
}
}
And the way of testing:
String jsonString = "{\"complaint_Map\":{\"1000067730\":\"3011351597604397\",\"1000067730-06\":\"10582576134561065\"}}";
ObjectMapper mapper = new ObjectMapper();
mapper.configure(DeserializationFeature.UNWRAP_ROOT_VALUE, true);
try {
Complaint complaint = mapper.readValue(jsonString, Complaint.class);
System.out.println(complaint);
} catch (Exception e) {
e.printStackTrace();
}
I used the following version (in Maven pom):
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.4.1.3</version>
</dependency>

How i can recover a Map of objects from json?

I have this class
public static class SomeClass {
public SomeClass(String field) {
this.field = field;
}
private final String field;
public String getField() {
return field;
}
}
I have also this test (edited)
#Test
public void testStringifyMapOfObjects() {
Map<String, SomeClass> original = Maps.newTreeMap();
original.put("first", new SomeClass("a"));
original.put("second", new SomeClass("b"));
String encoded = JsonUtil.toJson(original);
Map<String, SomeClass> actual = JsonUtil.fromJson(encoded, Map.class);
Assert.assertEquals("{'first':{'field':'a'},'second':{'field':'b'}}", encoded.replaceAll("\\s", "").replaceAll("\"", "'"));
Assert.assertEquals(original.get("first"), actual.get("first"));
}
The test fails with
junit.framework.AssertionFailedError: expected:<eu.ec.dgempl.eessi.facade.transport.test.TestToolTest$SomeClass#6e3ed98c> but was:<{field=a}>
at junit.framework.Assert.fail(Assert.java:47)
at junit.framework.Assert.failNotEquals(Assert.java:277)
at junit.framework.Assert.assertEquals(Assert.java:64)
at junit.framework.Assert.assertEquals(Assert.java:71)
at eu.ec.dgempl.eessi.facade.transport.test.TestToolTest.testStringifyMapOfObjects(TestToolTest.java:90)
Can I make json to properly serialize objects as the values of the map or should I use something else?
edited
public class JsonUtil {
private static final org.slf4j.Logger LOG = org.slf4j.LoggerFactory.getLogger(JsonUtil.class);
public static <T> String toJson(T data) {
ObjectMapper mapper = new ObjectMapper();
mapper.configure(Feature.INDENT_OUTPUT, true);
try {
return mapper.writeValueAsString(data);
} catch (IOException e) {
LOG.warn("can't format a json object from [" + data + "]", e);
return null;
}
//
// return Json.stringify(Json.toJson(data));
}
public static <T> T fromJson(String description, Class<T> theClass) {
try {
JsonNode parse = new ObjectMapper().readValue(description, JsonNode.class);
T fromJson = new ObjectMapper().treeToValue(parse, theClass);
return fromJson;
} catch (JsonParseException e) {
// throw new RuntimeException("can't parse a json object of type " + theClass.getName() + " from [" + description + "]", e);
LOG.warn("can't parse a json object from [" + description + "]", e);
return null;
} catch (JsonMappingException e) {
// throw new RuntimeException("can't parse a json object of type " + theClass.getName() + " from [" + description + "]", e);
LOG.warn("can't parse a json object from [" + description + "]", e);
return null;
} catch (IOException e) {
// throw new RuntimeException("can't parse a json object of type " + theClass.getName() + " from [" + description + "]", e);
LOG.warn("can't parse a json object from [" + description + "]", e);
return null;
}
}
}
You are running into a problem related to Java generics. To summarize, when deserializing data into a non-reifiable type (aka a type for which actual type information is not available at runtime) you need to use a supertype token. You can get more detail about what a supertype token is (and why you need to use one) by reading these SO posts:
Pass parameterized type to method as argument
Error using Jackson and JSON
Deserialize JSON to ArrayList using Jackson
And also from the Jackson documentation:
Data Binding With Generics
TypeReference Javadoc
The basic problem is that when you use a typical generic object, the actual type parameters for the object aren't available at runtime. Therefore Jackson doesn't know which actual class to instantiate and deserialize your data into.
The easiest way to get around the problem would be adding an overload to your JSON utility class, that accepts a type reference (as opposed to a Class<T>). For example:
public static <T> T fromJson(String json, TypeReference<T> typeRef) {
if(json == null || typeRef == null) return null;
return new ObjectMapper().readValue(json, typeRef);
}
To be used as such:
Map<String, SomeClass> actual = JsonUtil.fromJson(
encoded,
new TypeReference<Map<String, SomeClass>>(){});
I discovered that the simplest solution is to create a "container" class that will contain the map. This is working probably because the container has enough type details for the map, as opposed to the case when a map is used directly.
public static class SomeClass {
private final String field;
private SomeClass() {
this("wrong");
}
public SomeClass(String field) {
this.field = field;
}
public String getField() {
return field;
}
#Override
public String toString() {
return "SomeClass[" + field + "]";
}
}
public static class SomeClassContainer {
private final Map<String, SomeClass> all = Maps.newTreeMap();
public Map<String, SomeClass> getAll() {
return all;
}
}
After this ... the updated test is
#Test
public void testStringifyMapOfObjects() {
SomeClassContainer original = new SomeClassContainer();
original.getAll().put("first", new SomeClass("a"));
original.getAll().put("second", new SomeClass("b"));
String encoded = JsonUtil.toJson(original);
System.out.println(encoded);
SomeClassContainer actual = JsonUtil.fromJson(encoded, SomeClassContainer.class);
System.out.println(ObjectUtils.toString(actual));
Assert.assertEquals("{'all':{'first':{'field':'a'},'second':{'field':'b'}}}", encoded.replaceAll("\\s", "").replaceAll("[\"]", "'"));
Assert.assertEquals("class eu.ec.dgempl.eessi.facade.transport.test.TestToolTest$SomeClass", actual.getAll().get("first").getClass().toString());
Assert.assertEquals(original.getAll().get("first").toString(), actual.getAll().get("first").toString());
Assert.assertEquals(original.getAll().get("second").toString(), actual.getAll().get("second").toString());
}

Categories