I am trying to pass a JSON request to my server where the controller encounters an error while converting the JSON to POJO.
JSON Request
{
"request":[
{"name":"mac"},
{"name":"rosy"}
]
}
My controller function
#RequestMapping(value = "/namelist",
method = RequestMethod.POST,
consumes = { "application/json" },
produces = {"application/json"})
public ... postNameList(#RequestBody NameList names) {}
Public Class NameList extends ArrayList<Name> {}
Public Class Name { private name; ...}
Error
message: "Could not read JSON: Can not deserialize instance of
com.abc.xyz.mypackage.NameList out of START_OBJECT token at [Source:
org.eclipse.jetty.server.HttpConnection$Input#79aac24b{HttpChannelOverHttp#1d109942{r=1,a=DISPATCHED,uri=/namelist},HttpConnection#2cbdcaf6{FILLING},g=HttpGenerator{s=START},p=HttpParser{s=END,137
of 137}}; line: 1, column: 1]
I am not sure what's wrong with the code. I am fairly new to Spring so any help is appreciated.
Your POJO classes should look like this:
class Request {
private List<Name> request;
// getters, setters, toString, ...
}
class Name {
private String name;
// getters, setters, toString, ...
}
Usage:
#RequestMapping(value = "/namelist",
method = RequestMethod.POST,
consumes = { "application/json" },
produces = {"application/json"})
public ... postNameList(#RequestBody Request request) { ... }
I faced similar situation and then created utility to convert JSON objects into Java Objects. Hope this helps.
Here sample.json is the file you want to a Java Object
import com.sun.codemodel.JCodeModel;
import org.jsonschema2pojo.*;
import org.jsonschema2pojo.rules.RuleFactory;
import org.springframework.util.ResourceUtils;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.net.URL;
/**
* Created by Pratik Ambani
*/
class JsonToPojo {
public static void main(String[] args) {
String packageName = "com.practise";
File inputJson = null;
try {
inputJson = ResourceUtils.getFile("classpath:sample.json");
} catch (FileNotFoundException e) {
e.printStackTrace();
}
File outputPojoDirectory = new File("." + File.separator + "Generated Pojo");
outputPojoDirectory.mkdirs();
try {
new JsonToPojo().convert2JSON(inputJson.toURI().toURL(), outputPojoDirectory, packageName, inputJson.getName().replace(".json", ""));
} catch (IOException e) {
System.err.println("Encountered issue while converting to pojo: " + e.getMessage());
e.printStackTrace();
}
}
private void convert2JSON(URL inputJson, File outputPojoDirectory, String packageName, String className) throws IOException {
JCodeModel codeModel = new JCodeModel();
GenerationConfig config = new DefaultGenerationConfig() {
#Override
public boolean isGenerateBuilders() { // set config option by overriding method
return true;
}
#Override
public SourceType getSourceType() {
return SourceType.JSON;
}
};
SchemaMapper mapper = new SchemaMapper(new RuleFactory(config, new Jackson2Annotator(config), new SchemaStore()), new SchemaGenerator());
mapper.generate(codeModel, className, packageName, inputJson);
codeModel.build(outputPojoDirectory);
}
}
Related
I am using the Jackson to convert a List of XML to JSON after converting each event from the XML List<> I am storing the converted JSON into List<String>.
After all conversion, I have to create a new JSON with some outer elements and then finally add the converted JSON from previously-stored List<String>. Everything works fine but when I add the JSON from List<String> it also adds the \n, \ and all my formatting goes away. I am not sure why it does like this.
I tried to search and use the various methods mentioned but nothing seems to work so thought of posting here the same. Really sorry if found a duplicate.
Following is the code: (Please note: This is a sample direct code I have provided so that anyone who is trying can try directly to figure out the issue. However, another sample code with a complete workflow has been given below. Just to provide an indication how I am doing actually in my application. However, both are adding the \ and removing the formatting.)
I just want to know how to avoid adding these \ and other non-relevant characters to my Final JSON and add formatting to it.
public class Main {
public static void main(String[] args) throws IOException {
List<String> stringEvents = new ArrayList<>();
stringEvents.add("{\n" +
" isA : \"Customer\",\n" +
" name : \"Rise Against\",\n" +
" age : \"2000\",\n" +
" google:sub : \"MyValue\",\n" +
" google:sub : \"MyValue\"\n" +
"}");
JsonFactory factory = new JsonFactory();
StringWriter jsonObjectWriter = new StringWriter();
JsonGenerator generator = factory.createGenerator(jsonObjectWriter);
generator.writeStartObject();
generator.writeStringField("schema", "1.0");
generator.writeFieldName("eventList");
generator.writeStartArray();
stringEvents.forEach(event->{
try {
generator.writeObject(event);
} catch (IOException e) {
e.printStackTrace();
}
});
generator.writeEndArray();
generator.writeEndObject();
generator.close();
System.out.println(jsonObjectWriter.toString());
}
}
Following is the output I am getting:
{"isA":"Customer","name":"Rise Against","age":"2000"}
{"schema":"1.0","eventList":["{\"isA\":\"Customer\",\"name\":\"Rise Against\",\"age\":\"2000\"}","{\"isA\":\"Customer\",\"name\":\"Rise Against\",\"age\":\"2000\"}"]}
Following is my complete workflow and code:
I am reading the XML file and performing the unmarshalling of the XML to Customer.class.
I am performing the JSON Conversion and storing the converted JSON into List<String>.
After all conversion I am creating my Final JSON with all header information.
Following is my Customer.class:
#JsonTypeInfo(use = JsonTypeInfo.Id.NAME, include = JsonTypeInfo.As.PROPERTY, visible = true, property = "isA")
#JsonInclude(Include.NON_NULL)
#JsonIgnoreProperties(ignoreUnknown = true)
#XmlRootElement(name = "extension")
#XmlType(name = "extension", propOrder = {"name", "age"})
#XmlAccessorType(XmlAccessType.FIELD)
#Getter
#Setter
#AllArgsConstructor
#ToString
#NoArgsConstructor
public class Customer {
#XmlTransient
private String isA;
#XmlPath("customer/name/text()")
private String name;
#XmlPath("customer/age/text()")
private String age;
}
Following is my Unmarshaling and Json Creation class:
public class Unmarshalling {
public static void main(String[] args) throws JAXBException, XMLStreamException, FactoryConfigurationError, IOException {
final InputStream inputStream = Unmarshalling.class.getClassLoader().getResourceAsStream("customer.xml");
final XMLStreamReader xmlStreamReader = XMLInputFactory.newInstance().createXMLStreamReader(inputStream);
final Unmarshaller unmarshaller = JAXBContext.newInstance(Customer.class).createUnmarshaller();
final Customer customer = unmarshaller.unmarshal(xmlStreamReader, Customer.class).getValue();
final String jsonEvent = new ObjectMapper().writeValueAsString(customer);
System.out.println(jsonEvent);
List<String> stringEvents = new ArrayList<>();
stringEvents.add(jsonEvent);
stringEvents.add(jsonEvent);
JsonFactory factory = new JsonFactory();
StringWriter jsonObjectWriter = new StringWriter();
JsonGenerator generator = factory.createGenerator(jsonObjectWriter);
generator.writeStartObject();
generator.writeStringField("schema", "1.0");
generator.writeFieldName("eventList");
generator.writeStartArray();
stringEvents.forEach(event->{
try {
generator.writeObject(event);
} catch (IOException e) {
e.printStackTrace();
}
});
generator.writeEndArray();
generator.writeEndObject();
generator.close();
System.out.println(jsonObjectWriter.toString());
}
}
Following is my xml file:
<extension xmlns:google="https://google.com">
<customer>
<name>Rise Against</name>
<age>2000</age>
</customer>
</extension>
After trying for few more things I tried generator.writeRaw(event); and that worked.
import com.fasterxml.jackson.core.JsonFactory;
import com.fasterxml.jackson.core.JsonGenerator;
import java.io.IOException;
import java.io.StringWriter;
import java.util.ArrayList;
import java.util.List;
public class Main {
public static void main(String[] args) throws IOException {
List<String> stringEvents = new ArrayList<>();
stringEvents.add("{\n" +
" isA : \"Customer\",\n" +
" name : \"Rise Against\",\n" +
" age : \"2000\",\n" +
" google:sub : \"MyValue\",\n" +
" google:sub : \"MyValue\"\n" +
"}");
JsonFactory factory = new JsonFactory();
StringWriter jsonObjectWriter = new StringWriter();
JsonGenerator generator = factory.createGenerator(jsonObjectWriter);
generator.writeStartObject();
generator.writeStringField("schema", "1.0");
generator.writeFieldName("eventList");
generator.writeStartArray();
stringEvents.forEach(event->{
try {
generator.writeRaw(event);
} catch (IOException e) {
e.printStackTrace();
}
});
generator.writeEndArray();
generator.writeEndObject();
generator.close();
System.out.println(jsonObjectWriter.toString());
}
}
I have a javafx program that brings up a filechooser to allow a user to pick and image an display it to a grid view with an inserted caption that pops up after an image was pciked. I save both the filepath and the caption to different arraylists [for now] and my goal is to save both to xml file so that I can unmarshall it when the application is re opened so the images would still be there. Right now I just want to be able to save the two strings to an xml file and then figure out the rest later. I am currently able to run my code with no errors until I reach my stop method where I try to save every image and caption the user has added to the array lists.
My JAXB Annotation:
#XmlRootElement
#XmlAccessorType(XmlAccessType.FIELD)
public class ImageCap {
private String filePath;
private String caption;
public ImageCap() {
}
public ImageCap(String filePath, String caption) {
this.filePath = filePath;
this.caption = caption;
}
#Override
public String toString() {
return "ImageCap{" + "filePath=" + filePath + ", caption=" + caption + '}';
}
public String getFilePath() {
return filePath;
}
#XmlElement
public void setFilePath(String filePath) {
this.filePath = filePath;
}
public String getCaptions() {
return caption;
}
#XmlElement
public void setCaption(String caption) {
this.caption = caption;
}
}
And my main to test:
public static void main(String[] args) {launch(args);}
public void start(Stage primaryStage) throws JAXBException{
final JFXPanel bananarama = new JFXPanel();
//import the library (read))
// create the (initial) display
display.makeBrowseButton(primaryStage);
display.createDisplay(primaryStage);
// show user
primaryStage.show();
}#Override
public void stop() throws JAXBException{
File file = new File("file.xml");
JAXBContext jaxbContext = JAXBContext.newInstance(ImageCap.class);
Marshaller jaxbMarshaller = jaxbContext.createMarshaller();
//this.context = JAXBContext.newInstance(ImageCap.class);
//Marshaller marshaller = context.createMarshaller();
for(int i = 0; i < display.filePaths.size(); i++)
{
ImageCap imageCap = new ImageCap();
imageCap.setFilePath(display.filePaths.get(i));
imageCap.setCaption(display.captions.get(i).toString());
System.out.println(display.filePaths.get(i).toString());
try {
// output pretty printed
jaxbMarshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
jaxbMarshaller.marshal(imageCap, file);
} catch (JAXBException e) {
e.printStackTrace();
}
}
}
Here are my errors after the stop command:
this problem is related to the following location:
at public void ImageCap.setCaption(java.lang.String)
at ImageCap
this problem is related to the following location:
at private java.lang.String ImageCap.caption
at ImageCap
Class has two properties of the same name "filePath"
this problem is related to the following location:
at public java.lang.String ImageCap.getFilePath()
at ImageCap
this problem is related to the following location:
at private java.lang.String ImageCap.filePath
at ImageCap
but it specically cuts off at line 81 which is:
JAXBContext jaxbContext = JAXBContext.newInstance(ImageCap.class);
any ideas why?
If you have getter and setters for a field of the same name, then you need to use XmlAccessType.PROPERTY rather than XmlAccessType.FIELD:
#XmlRootElement
#XmlAccessorType(XmlAccessType.PROPERTY)
public static class ImageCap {
private String filePath;
private String caption;
...
Also, another problem that you will encounter is that you misspelled getCaptions() as plural, when it should be getCaption(). JAXB will complain about it as well.
Finally, by marshaling your file inside the loop, you are rewriting over and over the same file with the currently processed imageCap. If what you want is to marshall all imageCaps you need to put them on a List and marshall the List instead. In order to do that you'd need a new JAXB model class like:
#XmlRootElement(name = "myImageCapList")
class ImageCapList {
#XmlElement
List<ImageCap> imageCap;
public ImageCapList() {}
public ImageCapList(List<ImageCap> imageCaps) {
this.imageCap = imageCaps;
}
}
and you'd need to create an instance of this object wrapping your list of ImageCap objects (List<ImageCap>) and use it as the target to invoke the jaxbMarshaller.marshal method as shown in the following method:
public void imageCapsMarshal(List<ImageCap> imageCaps, File outFile) {
try {
jaxbMarshaller.marshal(new ImageCapList(imageCaps), outFile);
} catch (JAXBException e) {
// HANDLE EXCEPTIONS
}
}
also, you'll need to instantiate your JAXBContext appropriately:
jaxbContext = JAXBContext.newInstance(ImageCapList.class);
The following is a complete working demo of this for you to play with:
import java.io.File;
import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.IntStream;
import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBException;
import javax.xml.bind.Marshaller;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlRootElement;
public class JAXBMarshall {
private JAXBContext jaxbContext;
private Marshaller jaxbMarshaller;
public JAXBMarshall() throws JAXBException {
jaxbContext = JAXBContext.newInstance(ImageCapList.class);
jaxbMarshaller = jaxbContext.createMarshaller();
jaxbMarshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
}
public void imageCapsMarshal(List<ImageCap> imageCaps, File outFile) {
try {
jaxbMarshaller.marshal(new ImageCapList(imageCaps), outFile);
} catch (JAXBException e) {
// HANDLE EXCEPTIONS
}
}
public static void main(String[] args) throws JAXBException {
JAXBMarshall jaxbMarshaller = new JAXBMarshall();
File file = new File("file.xml");
List<ImageCap> imageCaps = IntStream.range(0, 10)
.mapToObj(idx -> new ImageCap("my/file/path/" + idx, idx + ". The Caption!"))
.collect(Collectors.toList());
jaxbMarshaller.imageCapsMarshal(imageCaps, file);
}
#XmlRootElement(name = "myImageCapList")
static class ImageCapList {
#XmlElement
List<ImageCap> imageCap;
public ImageCapList() {}
public ImageCapList(List<ImageCap> imageCaps) {
this.imageCap = imageCaps;
}
}
#XmlRootElement
static class ImageCap {
#XmlElement
String filePath;
#XmlElement
String caption;
public ImageCap() {}
public ImageCap(String filePath, String caption) {
this.filePath = filePath;
this.caption = caption;
}
#Override
public String toString() {
return "ImageCap{" + "filePath=" + filePath + ", caption=" + caption + '}';
}
}
}
Complete code on GitHub
Hope this helps.
I have two projects defined 1) GeneralOrm which is used for general purpose web services and 2)CompanyWS which includes company specific webservices.
I am using Spring and Hibernate 4 version
I am writing a GET request to pull information based on two parameters namely, employeeID and informationID. There can only be one employeeID and multiple
informationID. Based on a employeeID and Information ID, I want to display the employeeID, INFORMATION_ID, VALUE_DISPLAY_NAME and value_emp_ID in my JSON result.
My table EMP_METADATA in the database looks like the following:
column_Name Data_Type
EMPLOYEE_ID NUMBER
INFORMATION_ID NUMBER
VALUE_DISPLAY_NAME VARCHAR2(30 BYTE)
VALUE_EMP_ID NUMBER
Inside CompanyWS project, I have the GET request defined inside a controller in the following manner:
Inside package CompanyWS : edu.abc.company.controller
#RequestMapping(value="/get_em_metadata", method=RequestMethod.GET)
public String getEmpMetaData
(
#RequestParam(value="employee_id", defaultValue="0") Integer employeeID_,
#RequestParam(value="information_id", defaultValue="0") Integer informationID_
)
{
List<EmployeeMetaData> cvmetadata = null;
GetEmployeeResult result = new GetEmployeeResult();
try{
EmployeeMetaDataDao rmDao = (EmployeeMetaDataDao)context.getBean("EmployeeMetaDataDao");
List<EmployeeMetaData> rm = rmDao.findByEmpAndInfoId(employeeID_, informationID_);
if(rm != null) && (!rm.isEmpty()){
System.out.println("Checking Aug 31:"+rm); // This works and print outs on the console
}
} catch(Throwable th) {
th.printStackTrace();
result.setWebServiceStatus(th.getMessage(), false);
}
//return result.toJSON();
}
I am assuming that since my System.out.print statement is printing the following on the console after running the webservice,
Checking Aug 31:[edu.abc.company.orm.EmployeeMetaData#5963b830]
I am half way through and I just need to print the result in the JSON format. In order to print the result, I have defined GetEmployeeResult which is as follows and extending the WebServiceResult class. I am wondering do I need to use GetEmployeeResult and extend WebserviceResult class or I can directly use WebServiceResult class inside my controller to
print the results in JSON format? Any idea how should I proceed with the code for printing JSON inside controller. I have't used Hibernate before. Thanks in advance.
package edu.abc.company.json;
import java.util.List;
import edu.abc.company.domain.CvMetaDataList;
import edu.abc.company.domain.companyMetaDataList;
import edu.abc.company.util.WebServiceResult;
public class GetEmployeeResult extends WebServiceResult {
}
And here is the WebServiceResult class which is defined as follows:
package edu.abc.company.util;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature;
import edu.abc.company.domain.StatusMessage;
import edu.abc.company.json.Views;
// A generic object that can be used to return data and a standardized status message from a web service.
public class WebServiceResult {
protected StatusMessage webservice_status;
// C-tor
public WebServiceResult() {}
// C-tor
public WebServiceResult(StatusMessage webserviceStatus_) {
webservice_status = webserviceStatus_;
}
// Web service status
public StatusMessage getWebServiceStatus() {
return webservice_status;
}
public void setWebServiceStatus(String message_, boolean success_) {
webservice_status = new StatusMessage();
if (success_) {
webservice_status.setStatus(Constants.SUCCESS); // Constants is another class which has messages defined, not including here
webservice_status.setMessage(message_);
} else {
webservice_status.setStatus(Constants.ERROR);
webservice_status.setMessage(message_);
}
}
// Export the object's contents as JSON.
public String toJSON(boolean pretty_) {
String json = "";
try {
ObjectMapper objectMapper = new ObjectMapper();
objectMapper.configure(SerializationFeature.INDENT_OUTPUT, pretty_);
// Convert the object to JSON.
json = objectMapper.writerWithView(Views.Normal.class).writeValueAsString(this);
}
catch(Exception e) {
e.printStackTrace();
}
return json;
}
public String toJSON() {
return toJSON(true);
}
}
The following are just for reference purpose in case someone is interested in looking at it:
I have defined the EmployeeMetaData inside the GeneralOrm project as follows:
Inside package GeneralOrm : edu.abc.company.orm
package edu.abc.company.orm;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.Table;
#Entity
#Table(name="EMP_METADATA")
public class EmployeeMetaData
{
public int getEmployeeId() {
return employeeId;
}
public void setEmployeeId(int employeeId) {
this.employeeId = employeeId;
}
public int getInformationId() {
return informationId;
}
public void setInformationId(int informationId) {
this.informationId = informationId;
}
public String getValueDisplayName() {
return valueDisplayName;
}
public void setValueDisplayName(String valueDisplayName) {
this.valueDisplayName = valueDisplayName;
}
public int getValueempId() {
return valueempId;
}
public void setValueEmpId(int valueempId) {
this.valueempId = valueempId;
}
#Id
#Column(name="EMPLOYEE_ID")
private int employeeId;
#Column(name="INFORMATION_ID")
private int informationId;
#Column(name="VALUE_DISPLAY_NAME")
private String valueDisplayName;
#Column(name="VALUE_EMP_ID")
private int valueempId;
}
I have defined the EmployeeMetaDataDao inside the GeneralOrm project as follows:
Inside project GeneralOrm : edu.abc.company.orm.dao
package edu.abc.company.orm.dao.impl;
import java.util.List;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import edu.abc.company.orm.EmployeeMetaData;
import edu.abc.company.orm.dao.EmployeeMetaDataDao;
import edu.abc.company.util.Util;
public class EmployeeMetaDataDaoImpl implements EmployeeMetaDataDao {
public void setSessionFactory(SessionFactory sessionFactory) {
this.sessionFactory = sessionFactory;
}
#SuppressWarnings("unchecked")
public List<EmployeeMetaData> list() {
logger.debug("Starting EmployeeMetaDataDaoImpl.list() .....");
Session session = null;
List<EmployeeMetaData> EmployeeMetaData = null;
try {
session = this.sessionFactory.openSession();
EmployeeMetaData = session.createQuery("FROM EmployeeMetaData").list();
} catch(Exception ex) {
ex.printStackTrace();
} finally {
session.close();
}
logger.debug("Completed EmployeeMetaDataDaoImpl.list() .....");
return EmployeeMetaData;
}
public List<EmployeeMetaData> findByEmpAndInfoId(int employee_id, int information_id) {
List<EmployeeMetaData> EmployeeMetaData = null;
Session session = null;
try {
session = this.sessionFactory.openSession();
EmployeeMetaData = session.createQuery("FROM EmployeeMetaData WHERE information_id = '" + information_id + "'" + " AND company_id = '" + employee_id + "'").list();
} catch(Exception ex) {
ex.printStackTrace();
} finally {
session.close();
}
return EmployeeMetaData;
}
// Main method goes here
public static void main(String[] args)
{
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("ApplicationContext.xml");
try{
EmployeeMetaDataDao rpcDao = (EmployeeMetaDataDao)context.getBean("EmployeeMetaDataDao");
List<EmployeeMetaData> EmployeeMetaData = rpcDao.list();
if ((EmployeeMetaData != null) && EmployeeMetaData.size() > 0) {
for (int i=0;i<EmployeeMetaData.size();i++) {
EmployeeMetaData re = EmployeeMetaData.get(i);
}
}
context.close();
} catch(Throwable th) {
th.printStackTrace();
} finally {
context.close();
}
}
private SessionFactory sessionFactory;
private static final Logger logger = LoggerFactory.getLogger(EmployeeMetaDataDaoImpl.class);
}
I created a model:
public class UserRequest extends DefaultRequest {
public String username;
public String password;
public String id;
public UserRequest(String username, String password) {
this.username = username;
this.password = password;
}
}
And I'm calling it like:
//code truncated
UserRequest userRequest = new UserRequest(username,password);
response = getRestClient().sysInitApp(userRequest).execute();
//code truncated
And then I print out request body, instead of:
{
"username":"farid",
"password":"passfarid",
"id":null
}
I get:
{
"username":"farid",
"password":"passfarid"
}
I would appreciate any help with this issue.
from the GsonBuilder javadocs... you can use GsonBuilder to construct your Gson instance, and opt in to have null values serialized as so:
Gson gson = new GsonBuilder()
.serializeNulls()
.create();
Not too familiar with Gson, but I don't think Gson would write null values to an json file. If you initialize the id like:
String id = "";
you may get an empty string in there. But you will not get a null value into a .xml file.
An example of how to enforce outputting values even if null. It will output the empty string (or "{}" if an object) instead of null and ignore transients:
package unitest;
import java.io.IOException;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.google.gson.TypeAdapter;
import com.google.gson.TypeAdapterFactory;
import com.google.gson.reflect.TypeToken;
import com.google.gson.stream.JsonReader;
import com.google.gson.stream.JsonToken;
import com.google.gson.stream.JsonWriter;
public class TheResponse<T> {
private String status;
private String message;
private T data;
transient private String resource;
public static void main(String[] args) {
TheResponse<String> foo = new TheResponse<String>();
//TheResponse<Baz> foo = new TheResponse<Baz>();
foo.status = "bar";
foo.data = "baz";
Gson gson = new GsonBuilder().registerTypeAdapter(TheResponse.class, new GenericAdapter()).create();
System.out.println(gson.toJson(foo).toString());
}
public static class GenericAdapter extends TypeAdapter<Object> {
#Override
public void write(JsonWriter jsonWriter, Object o) throws IOException {
recursiveWrite(jsonWriter, o);
}
private void recursiveWrite(JsonWriter jsonWriter, Object o) throws IOException {
jsonWriter.beginObject();
for (Field field : o.getClass().getDeclaredFields()) {
boolean isTransient = Modifier.isTransient(field.getModifiers());
if (isTransient) {
continue;
}
Object fieldValue = null;
try {
field.setAccessible(true);
fieldValue = field.get(o);
} catch (IllegalArgumentException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
jsonWriter.name(field.getName());
if (fieldValue != null && fieldValue.getClass() != String.class) {
recursiveWrite(jsonWriter, fieldValue);
continue;
}
if (fieldValue == null) {
if (field.getType() == String.class)
jsonWriter.value("");
else {
jsonWriter.jsonValue("{}");
}
} else {
jsonWriter.value(fieldValue.toString());
}
}
jsonWriter.endObject();
}
#Override
public Object read(JsonReader jsonReader) throws IOException {
// todo
return null;
}
}
}
I am working on a little todolist-program and i'm getting a weird bug that i never had before. I have 4 classes: 1 POJO class that contains the todo-data:
public class Todo implements Comparable {
private String title;
private String task;
private boolean done;
public Todo(String title, String task) {
this.title = title;
this.task = task;
}
public String getTitle() {
return title;
}
public void setTitle(String newTitle) {
title = newTitle;
}
public String getTask() {
return task;
}
public void setTask(String newTask) {
task = newTask;
}
public boolean isDone() {
return done;
}
public void setDone(boolean isDone) {
done = isDone;
}
public int compareTo(Object obj) {
Todo todo = (Todo) obj;
return getTitle().compareTo(todo.getTitle());
}
public String toString() {
StringBuilder sb = new StringBuilder();
sb.append("Todo {\n");
sb.append("Title: \"");
sb.append(getTitle() + "\";\n");
sb.append("Task: \"");
sb.append(getTask() + "\";\n");
sb.append("}");
return sb.toString();
}
}
Then I have a class that stores and loads my todos:
import java.io.BufferedReader;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.util.List;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
public class ListStorage {
Gson gson = new GsonBuilder().setPrettyPrinting().create();
FileWriter writer;
BufferedReader reader;
public void storeList(List list, String filename) throws IOException {
String json = gson.toJson(list);
writer = new FileWriter(filename);
writer.write(json);
writer.close();
}
public List loadList(String filename) throws FileNotFoundException {
reader = new BufferedReader(new FileReader(filename));
List list = gson.fromJson(reader, List.class);
return list;
}
}
Then I have a 'Manager' class that is basically my controller:
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
public class Manager {
private List<Todo> todos = new ArrayList<>();
private ListStorage storage = new ListStorage();
public List getTodos() {
return todos;
}
public void setTodos(List newTodos) {
todos = newTodos;
}
public ListStorage getStorage() {
return storage;
}
public void add(String title, String task) {
todos.add(new Todo(title, task));
sort();
try {
storage.storeList(todos, "todos");
} catch(Exception e) {
e.printStackTrace();
}
}
public void remove(int index) {
todos.remove(index);
sort();
try {
storage.storeList(todos, "todos");
} catch(Exception e) {
e.printStackTrace();
}
}
private void sort() {
Collections.sort(todos);
}
}
And finally there is my main-class for testing my code (The bug seems to be here):
class CLITodo {
public static void main(String[] args) {
Manager man = new Manager();
man.add("Hello", "Bye");
man.add("Foo", "Bar");
try {
man.setTodos(man.getStorage().loadList("todos"));
} catch(Exception e) {
}
java.util.List<Todo> todos = man.getTodos();
for (Todo t : todos) {
System.out.println(t);
}
}
}
The error message I get when I leave the <Todo> in CLITodo class is:
Exception in thread "main" java.lang.ClassCastException: com.google.gson.internal.LinkedTreeMap cannot be cast to Todo at CLITodo.main(CLITodo.java:13)
When I remove <Todo> in CLITodo I get this error:
CLITodo.java:13:19: error: incompatible types
for (Todo t : todos) {
^
required: Todo
found: Object
Why does this error occur? My Manager classes getTodos()-Method returns a List of type Todo yet the compiler tells me that it is just an Object (which it is of course but it is a collection as well, which should actually work).
This is the first time this error occured and I really can't seem to find what is causing it.
When you don't specify what nested type to use to deserialize your JSON, like you do here
List list = gson.fromJson(reader, List.class); // All it knows is that the root json is a List
Gson uses LinkedTreeMap.
What you really want is
List list = gson.fromJson(reader, new TypeToken<List<Todo>>(){}.getType());