JSON to POJO as Object class using jackson java - java

I'm trying to use DTO to JSON (Write in Json file) and JSON to DTO (Read from JSON file) as a common methods (Generic method to be used by different pojo write/read operations)
Inorder to use as common method, i'm using return type as object.
Below my code
public String dtoToJSON(String appName, Object obj) throws IOException {
ObjectMapper mapper = new ObjectMapper();
mapper.enable(SerializationFeature.INDENT_OUTPUT);
String postJson = mapper.writeValueAsString(obj);
System.out.println(postJson);
// Save JSON string to file
FileOutputStream fileOutputStream = new FileOutputStream("post.json");
mapper.writeValue(fileOutputStream, obj);
fileOutputStream.close();
return appName;
}
public Object jsonToDto() throws IOException {
ObjectMapper mapper = new ObjectMapper();
// Read JSON file and convert to java object
InputStream fileInputStream = new FileInputStream("post.json");
Object obj = mapper.readValue(fileInputStream, Object.class);
fileInputStream.close();
return obj;
}
I'm able to run DTO to JSON (Write in Json file) successfully but when i try to run JSON to DTO (Read from JSON file) i get ClassCastException
My exception:
thread "main" java.lang.ClassCastException: Cannot cast java.util.LinkedHashMap to com.me.dto.Post
My main method
public static void main(String[] args) throws IOException {
Transform ts=new Transform();
Post post=(Post)ts.jsonToDto();
// print post object
System.out.println("Printing post details");
System.out.println(post.getId());
System.out.println(post.getTitle());
System.out.println(post.getDescription());
System.out.println(post.getContent());
System.out.println(post.getLastUpdatedAt());
System.out.println(post.getPostedAt());
}
}
Please let me know if i'm wrong.
Thanks in advance.

It says thread "main" java.lang.ClassCastException: Cannot cast java.util.LinkedHashMap to com.me.dto.Post, which means your ts.jsonToDto() returns a LinkedHashMap and cannot be cast to your DTO.
You can refer here to have more information.
The issue's coming from Jackson. When it doesn't have enough information on what class to deserialize to, it uses LinkedHashMap.
Since you're not informing Jackson of the element type of your ArrayList, it doesn't know that you want to deserialize into an ArrayList of Accounts. So it falls back to the default.
They also gave you solutions there.

If you debug the code ,you will see the code below in class
com.fasterxml.jackson.databind.deser.std.UntypedObjectDeserializer
....
switch (p.getCurrentTokenId()) {
case JsonTokenId.ID_START_OBJECT:
{
JsonToken t = p.nextToken();
if (t == JsonToken.END_OBJECT) {
return new LinkedHashMap<String,Object>(2);
}
}
case JsonTokenId.ID_FIELD_NAME:
return mapObject(p, ctxt);
....
From the above ,we can see ,if your Class is java.lang.Object ,it will perform the case JsonTokenId.ID_START_OBJECT,and return a LinkedHashMap as a result.

Related

Is there a way to fetch Json values within Json?

I've got a Scenario Entity in my Spring boot app
private Long id;
private String scenarioName;
private HashMap<Integer, Object> scenarioAttributes;
Let's say we create a Scenario entity with following Json:
{
"scenarioName":"scenario1",
"scenarioAttributes":{
"1" : {
"goToURL":"https://google.com/"
},
"2" : {
"assertCurrentUrl":"https://google.com/"
}
}
}
In my ExecutorService, I've got following code:
public List<Object> getStepsFromScenarioAttributesValues(Long scenarioId){
List<Object> response = new ArrayList<>();
Scenario scenario = scenarioService.getScenario(scenarioId);
HashMap<Integer, Object> steps = scenario.getScenarioAttributes();
for (Map.Entry<Integer, Object> set : steps.entrySet()){
response.add(set.getValue());
System.out.println(response);
// prints out
//[{goToURL=https://google.com/}, {assertCurrentUrl=https://google.com/}]
}
return response;
}
public void executeSteps(List<Object> response){
for (Object obj : response){
JsonObject jsonObj = (JsonObject) obj;
if (jsonObj.has("goToUrl")) {
//goes to url
driverService.goToUrl(String.valueOf(jsonObj.get("goToUrl")));
return;
} else if (jsonObj.has("assertCurrentUrl")) {
//asserts cur url with value
driverService.assertCurrentUrl(String.valueOf(jsonObj.get("assertCurrentUrl")));
return;
}
}
}
public String executeScenario(Long scenarioId){
executeSteps(getStepsFromScenarioAttributesValues(scenarioId));
return "Scenario" + scenarioId + " has been executed successfully";
}
I've got a GetMapping for single scenario as follows:
#GetMapping("/scenarios/{id}/execute")
public List<Object> executeScenario(#PathVariable Long id){
return executorService.getStepsFromScenarioAttributesValues(id);
}
Which, upon sending one sample scenario and entering a site, provides us with, you guessed it, a List containing an Object, which is a Json.
Unfortunately, if I want to call executeSteps() function which has a list of Objects, I cannot do it since an Object is not a JsonObject.
I thought simple JsonObject jsonObj = (JsonObject) obj; would do a trick, but I'm greeted with
class java.util.LinkedHashMap cannot be cast to class com.google.gson.JsonObject (java.util.LinkedHashMap is in module java.base of loader 'bootstrap'; com.google.gson.JsonObject is in unnamed module of loader 'app')
How can I approach fetching values from scenarioAttributes Jsons to further interpret them?
Are there unnecessary lines of code that I could get rid of?
Feel free to point out my wrongdoings, just starting my journey with Spring
public class Scenario {
private String scenarioName;
private HashMap<Integer, Object> scenarioAttributes;
}
Use object mapper to print class object to JSON:
ObjectMapper objectMapper = new ObjectMapper();
Scenario scenario = objectMapper.readValue(response, Scenario.class);
When you say Object, which is a Json what do you mean by "Json"? Json is not a class. I assume that your Object is an instance of class String that contains text in Json format. Your exception though points out that it could be a Map. I would still assume that it is a String that holds Json. In your code you attempt to work with class JSONObject which is part of json-simple library. While it is a nice training library I would suggest that you don't use it. BTW great site about JSON with the list of most libraries for most languages is: https://www.json.org/json-en.html. Best options are (IMHO) are Jackson-Json (also known as Faster XML) or Gson (by Google) (Here is their user guide site). To parse your Json text to a class instance you can use ObjectMapper class which is part of Jackson-Json library. For example
public <T> T readValue(String content,
TypeReference valueTypeRef)
throws IOException,
JsonParseException,
JsonMappingException
See Javadoc. But I also may suggest a very simplified JsonUtils class which is a thin wrapper over Jackson-Json library. This class is part of Open-source library called MgntUtils written and maintained by me. Your code may be as simple as follows:
Scenario scenario;
try {
scenario = JsonUtils.readObjectFromJsonString(response, Scenario.class);
} catch(IOException ioe) {
....
}
Here is JsonUtils javadoc. MgntUtils library can be obtained as Maven artifact here or on Github (including source code and Javadoc)

How to serialize an object and read it into JsonNode?

I have a simple Java object that has a String property that contains serialized java objects:
#Data
private class Test {
int id;
String data;
}
The data is created with
test.setData(mapper.writeValueAsString(anyJavaObjectThatShouldBeSerialized));
Then I send the Test Object to another server, where I want to deserialize the object from the data property into a JSONNode:
JsonNode node = mapper.readTree(test.getData());
The problem is, that the result of readTree is a TextNode and not a JsonNode. Therefore I can not access the properties of the serialized data.
The problem might be that the serialized object is interpreted as a String because when I print it it has quotationsmarks around it:
"{\"id\":39,\"name\":\"Im a test value\"}"
How do I get a JsonNode from the String that contains serialized objects? Please note that the data can be ANY Java object, that's why I serialized it into a String.
Make a double deserialization - first deserialize the string payload to json, then deserialize the json to class, or JsonNode in your case.
public class Temp {
public static void main(String[] args) throws Exception {
String payload = //your string payload;
ObjectMapper mapper = new ObjectMapper();
String json = mapper.readValue(payload, String.class);
JsonNode node = mapper.readTree(json);
}
}
As you noted, the cause is that you are making double serialization - first when setting the data field, then again when sending data to the other server.
You can avoid this double serialization/deserialization by making data an object.
#Data
class Test {
int id;
Object data;
}
test.setData(anyJavaObjectThatShouldBeSerialized);
Like this it will be serialized like JsonObject.
{
"id": 39,
"name": "Im a test value"
}

org.hibernate.LazyInitializationException after using HibernateProxyTypeAdapter for Gson

I was having an issue with Gson because it can't pass to Json HibernateProxy objects, so I have followed this guide: link
This solve the typeAdapter problem with Gson, but now I'm getting the following exception:
org.hibernate.LazyInitializationException: could not initialize proxy - no Session
I have been searching how to solve this but the solutions that I have found don't work in this case.
This is my code:
List<ContratoSolicitudFull> returnValue =
new SomeBL().getData(id, null, null,currentUserId);
Type type = new TypeToken<List<ContratoSolicitudFull>>() {}.getType();
Gson gson = new GsonBuilder()
.registerTypeAdapter(DateTime.class, new DateTimeJsonConverter())
.registerTypeAdapter(LocalDate.class, new LocalDateJsonConverter())
.registerTypeAdapterFactory(HibernateProxyTypeAdapter.FACTORY)
.create();
String jsonValue = gson.toJson(returnValue, type); //Here is where it fail
Any idea?
By serialization to json, you are accessing unfetched data outside of a session context.
If you used exactly the same code as in your link then, try to change write method to this.
#SuppressWarnings({"rawtypes", "unchecked"})
#Override
public void write(JsonWriter out, HibernateProxy value) throws IOException {
//avoid serializing non initialized proxies
if (value == null || !Hibernate.isInitialized(value)) {
out.nullValue();
return;
}
// Retrieve the original (not proxy) class
Class<?> baseType = Hibernate.getClass(value);
// Get the TypeAdapter of the original class, to delegate the serialization
TypeAdapter delegate = context.getAdapter(TypeToken.get(baseType));
// Get a filled instance of the original class
Object unproxiedValue = ((HibernateProxy) value).getHibernateLazyInitializer()
.getImplementation();
// Serialize the value
delegate.write(out, unproxiedValue);
}
|| !Hibernate.isInitialized(value) was added to check if collection was initialized, and if not avoids accessing it.

Which class of Jackson library should I use to parse JSON data?

My JSON data:
[
"Gi 1/1",
{
"Shutdown":true,
"Speed":"force10ModeFdx",
"AdvertiseDisabled":0,
"MediaType":"dual",
"FC":"off",
"MTU":9600,
"ExcessiveRestart":false,
"PFC":0
}
]
Controlller class method:
public #ResponseBody void setSwitchPortInterfaceInfo(#RequestBody JsonNode jsonNode)
throws JsonProcessingException, IOException { }
When calling this method only "Gi 1/1" got parsed.
Which class do I need to pass as argument to parse complete JSON object?
The JSON Data represent in the question is not present in correct format. We need to convert it into proper version than only parse the Same.
Ideal way of declaring JSON structure is:
[{"key":"value"},{"key":"value"},{"key":"value"}]
ObjectMapper objectMapper = new ObjectMapper();
String strToParse = getString(); // put the string you want to parse
JsonNode node = objectMapper.readValue(strToParse, JsonNode.class);
System.out.println(node.get(0));
System.out.println(node.get(1));

Spring Boot REST application with Jackson - how to handle arbitrary/unknown xml structures passed in?

I am working with a Spring Boot REST application. We are using jackson to handle deserialization of XML as well as JSON passed in the request body. An example of an expected request body looks like this:
<formInput><formNum>999999</formNum><documentData>Completely unknown data structure here!</documentData></formInput>
In the documentData element, we will have a structure that is completely arbitrary/unknown on the server side. We don't care about the structure, because we only want to pass the xml that is nested in documentData on to another service.
The POJO that we are trying to map the request body onto looks like this:
#JsonDeserialize(using=FormInputJsonDeserializer.class)
public class FormInput {
private String formNum
private String documentData
public String getFormNum() {
return formNum
}
public void setFormNum(String formNum) {
this.formNum = formNum
}
public String getDocumentData() {
return documentData;
}
public void setDocumentData(String documentData) {
this.documentData = documentData;
}
}
The custom JsonDeserializer that we are trying to write:
public class FormInputJsonDeserializer extends JsonDeserializer<FormInput> {
#Override
public FormInput deserialize(JsonParser parser, DeserializationContext context)
throws IOException, JsonProcessingException {
FormInput formInput = new FormInput();
String fieldName
JsonToken currentToken
while (parser.nextToken() != null) {
currentToken = parser.getCurrentToken()
if (currentToken.equals(JsonToken.END_OBJECT)) {
continue
}
fieldName = parser.getCurrentName()
// formNum handling not written yet
if ("documentData".equalsIgnoreCase(fieldName)) {
if (parser.getCurrentToken().equals(JsonToken.START_OBJECT)) {
// we are at the start of documentData, and we need to capture the
// entire documentData node as a String since we don't know
// its structure
JsonFactory jfactory = new JsonFactory()
StringWriter jsonStringWriter = new StringWriter()
JsonGenerator jGen = jfactory.createGenerator(jsonStringWriter)
jGen.copyCurrentStructure(parser) // points to END_OBJECT after copy
jGen.close()
String documentDataJsonStr = jsonStringWriter.getBuffer().toString()
println("documentDataJsonStr: " + documentDataJsonStr)
}
}
}
// rest of code omitted
}
}
As I say, if the request body is xml, ideally I'd like to just keep it formatted as xml and assign that to the documentData String property. However I came up with the above custom deserialization code by following some other examples on StackOverflow. This parsing code ends up converting documentData to a JSON formatted String. Since I didn't know how to pass through the raw XML and get it mapped to the String property, I thought I could just convert the JSON formatted String back to a XML formatted String. A problem arises when we pass in a XML structure like this:
<formInput><formNum>9322</formNum><documentData><repeatLevel><subForm1><GROSS_DISTR> 13,004.31</GROSS_DISTR><GROSS_DISTR> 13,004.31</GROSS_DISTR><GROSS_DISTR> 13,004.31</GROSS_DISTR></subForm1></repeatLevel><repeatLevel><subForm1><GROSS_DISTR> 38,681.37</GROSS_DISTR><GROSS_DISTR> 38,681.37</GROSS_DISTR><GROSS_DISTR> 38,681.37</GROSS_DISTR></subForm1></repeatLevel></documentData></formInput>
After documentData is parsed in the deserialize method, the println statement shows the parsed JSON String as:
{"repeatLevel":{"subForm1":{"GROSS_DISTR":" 13,004.31","GROSS_DISTR":" 13,004.31","GROSS_DISTR":" 13,004.31"}},"repeatLevel":{"subForm1":{"GROSS_DISTR":" 38,681.37","GROSS_DISTR":" 38,681.37","GROSS_DISTR":" 38,681.37"}}}
This is actually not strictly valid JSON, due to the duplicate keys. I would have hoped that these would have been converted to JSON arrays, but that is not the case. So, I am unable to turn around and use something like the JSON.org libraries (JsonObject and XML) to convert the JSON String back to XML format (get an exception with a "duplicate key" error).
Does anybody have any suggestions or strategies for handling our situation?
You could try to use a JSONObject, add the #JsonIgnoreProperties("documentData") tag and extract documentData seperately from the raw data using substring()

Categories