I am using SpringBoot and trying to deserialize JSON like:
{
"userId": "Dave",
"queryResults": {
"id": "ABC",
"carData": {.....},
"carId": "Honda",
"status": 0,
"model": "X"
}
}
, into MyRequestModel clas:
public class MyRequestModel {
private String userId;
private String: queryResults;
}
, that is received as #RequestBody parameter in my #PostMapping method that looks like:
#PostMapping
public String postDate(#RequestBody MyRequestModel data) {
...
return "posted";
}
The above queryResults field is supposed to be stored as a CLOB in a database.
Problem I am having is that if I send this JSON to hit my endpoint (PostMapping) method, it cannot deserialize it into MyRequestModel and I get this error:
Cannot deserialize instance of java.lang.String out of START_OBJECT token
at [Source: (PushbackInputStream); line: 3, column: 18] (through reference chain: MyRequestModel["queryResults"])]
I guess the real answer to your question is: if you NEED the queryResults property to be a String, then implement a custom deserializer.
If not, then, use one of the alternatives that Jonatan and Montaser proposed in the other answers.
Implementing a custom deserializer within Spring Boot is fairly straightforward, since Jackson is its default serializer / deserializer and it provides a easy way to write our own deserializer.
First, create a class that implements the StdDeserializer<T>:
MyRequestModelDeserializer.java
public class MyRequestModelDeserializer extends StdDeserializer<MyRequestModel> {
public MyRequestModelDeserializer() {
this(null);
}
public MyRequestModelDeserializer(Class<?> vc) {
super(vc);
}
#Override
public MyRequestModel deserialize(JsonParser p, DeserializationContext ctxt)
throws IOException, JsonProcessingException {
JsonNode node = p.getCodec().readTree(p);
String userId = node.get("userId").asText();
String queryResults = node.get("queryResults").toString();
MyRequestModel model = new MyRequestModel();
model.setQueryResults(queryResults);
model.setUserId(userId);
return model;
}
}
Second, mark your class to be deserialized using your custom deserializer by using the #JsonDeserialize annotation:
MyRequestModel.java
#JsonDeserialize(using = MyRequestModelDeserializer.class)
public class MyRequestModel {
private String userId;
private String queryResults;
}
It's done.
queryResults is a String on Java side but it is an Object on JSON side.
You will be able to deserialize it if you send it in as a String:
{
"userId": "Dave",
"queryResults": "foo"
}
or if you create classes that maps to the fields:
public class MyRequestModel {
private String userId;
private QueryResults queryResults;
}
public class QueryResults {
private String id;
private CarData carData;
private String carId;
private Integer status;
private String model;
}
or if you serialize it into something generic (not recommended):
public class MyRequestModel {
private String userId;
private Object queryResults;
}
public class MyRequestModel {
private String userId;
private Map<String, Object> queryResults;
}
public class MyRequestModel {
private String userId;
private JsonNode queryResults;
}
You have two options to deserialize this request:-
change the type of queryResults to Map<String, Object>, it will accepts everything as an object of key and value. (Not recommended)
public class MyRequestModel {
private String userId;
private Map<String, Object> queryResults;
}
You have to create a class that wraps the results of queryResults as an object.
class QueryResult {
private String id;
private Map<String, Object> carData;
private String carId;
private Integer status;
private String model;
public QueryResult() {}
public QueryResult(String id, Map<String, Object> carData, String carId, Integer status, String model) {
this.id = id;
this.carData = carData;
this.carId = carId;
this.status = status;
this.model = model;
}
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public Map<String, Object> getCarData() {
return carData;
}
public void setCarData(Map<String, Object> carData) {
this.carData = carData;
}
public String getCarId() {
return carId;
}
public void setCarId(String carId) {
this.carId = carId;
}
public Integer getStatus() {
return status;
}
public void setStatus(Integer status) {
this.status = status;
}
public String getModel() {
return model;
}
public void setModel(String model) {
this.model = model;
}
}
and make the type of queryResult as shown:-
public class MyRequestModel {
private String userId;
private QueryResult queryResults;
}
Related
How can I save Raw Json as String in the MsSql db with the POST request - using Jackson ObjectMapper to convert the string to Json but not able to change raw json into string?
{
"id": 1,
"someName":"someName",
"json": {
"title": "example glossary",
"GlossDiv": {
"title": "S",
"GlossTerm": "Standard Generalized Markup Language"
}
},
"anotherjson":{
"name":"someone",
"age": 121
},
"somedate":"03-11-2019.00:00:00"
}
How can I save this save json as integer, varchar, string, string, date column in the db?
1,someName, "{"title": "example glossary","GlossDiv": {"title": "S","GlossTerm": "Standard Generalized Markup Language"}","{"name":"someone","age": 121}", 03-11-2019.00:00:00.
** Update **
For simplicity here is the simple json
{
"id":1,
"jsonObjectHolder":{
"name": "Name",
"age" : 404
}}
Controller:
#PostMapping("/postJson")
public void postJson(#RequestBody TryJson tryJson) {
tryJsonService.postJson(tryJson);
}
Service:
public void postJson(TryJson tryJson) {
tryJsonRepository.save(tryJson);
}
Repo:
public interface TryJsonRepository extends CrudRepository<TryJson, Integer> {
}
Model:
#Entity
#Table(name = "TryJson")
public class TryJson {
#Id
#Column(name = "id")
private Integer id;
#JsonIgnore
#Column(name = "json_column")
private String jsonColumn;
#Transient
private JsonNode jsonObjectHolder;
public TryJson() {
}
public TryJson(Integer id, String jsonColumn) {
this.id = id;
this.jsonColumn = jsonColumn;
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public JsonNode getJsonObjectHolder() {
return jsonObjectHolder;
}
public void setJsonObjectHolder(JsonNode jsonObjectHolder) {
this.jsonObjectHolder = jsonObjectHolder;
}
public String getJsonColumn() {
return this.jsonObjectHolder.toString();
}
public void setJsonColumn(String jsonColumn) throws IOException {
ObjectMapper mapper = new ObjectMapper();
this.jsonObjectHolder = mapper.readTree(jsonColumn);
}
#Override
public String toString() {
return String.format("TryJson [Id=%s, JsonColumn=%s, jsonObjectHolder=%s]", id, jsonColumn, jsonObjectHolder);
}
}
http://localhost:8080/api/postJson
ID JSON_COLUMN
1 null
Not sure what I am missing here. I do get jsonObjectHolder populated during the debugging but then still I get NULL
TryJson [Id=1, JsonColumn=null, jsonObjectHolder={"name":"Name","age":404}]
Update 2
I am getting null pointer exception.
Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception [Request processing failed; nested exception is org.springframework.orm.jpa.JpaSystemException: Exception occurred inside getter of com.example.tryjson.tryjson.model.TryJson.jsonColumn; nested exception is org.hibernate.PropertyAccessException: Exception occurred inside getter of com.example.tryjson.tryjson.model.TryJson.jsonColumn] with root cause
java.lang.NullPointerException: null
at com.example.tryjson.tryjson.model.TryJson.getJsonColumn(TryJson.java:52) ~[classes/:na]
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[na:1.8.0_192]
Here is my new model
#Entity
#Table(name = "TryJson")
public class TryJson {
private Integer id;
#Transient
private JsonNode jsonObjectHolder;
public TryJson() {
}
public TryJson(Integer id, JsonNode jsonObjectHolder) {
this.id = id;
this.jsonObjectHolder = jsonObjectHolder;
}
#Id
#Column(name = "id")
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
#Transient
public JsonNode getJsonObjectHolder() {
return jsonObjectHolder;
}
public void setJsonObjectHolder(JsonNode jsonObjectHolder) {
this.jsonObjectHolder = jsonObjectHolder;
}
#Column(name = "json_column")
public String getJsonColumn() {
return this.jsonObjectHolder.toString();
}
public void setJsonColumn(String jsonColumn) throws IOException {
ObjectMapper mapper = new ObjectMapper();
this.jsonObjectHolder = mapper.readTree(jsonColumn);
}
}
You could define a JsonNode json property to hold the part you want to persist as text, then mark it as #Transient so JPA does not try to store it on database. However, jackson should be able to translate it back and forward to Json.
Then you can code getter/setter for JPA, so you translate from JsonNode to String back and forward. You define a getter getJsonString that translate JsonNode json to String. That one can be mapped to a table column, like 'json_string', then you define a setter where you receive the String from JPA and parse it to JsonNode that will be avaialable for jackson.
Do not forget to add #JsonIgnore to getJsonString so Jackson does not try to translate to json as jsonString.
#Entity
#Table(name = "request")
public class Request {
private Long id;
private String someName;
#Transient
private JsonNode json;
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
public Long getId() {
return id;
}
#Column(name ="someName")
public String getSomeName() {
return name;
}
public void setSomeName(String name) {
this.name = name;
}
public void setId(Long id) {
this.id = id;
}
// Getter and setter for name
#Transient // This is for Jackson
public JsonNode getJson() {
return json;
}
public void setJson(JsonNode json) {
this.json = json;
}
#Column(name ="jsonString")
public String getJsonString() { // This is for JPA
return this.json.toString();
}
public void setJsonString(String jsonString) { // This is for JPA
// parse from String to JsonNode object
ObjectMapper mapper = new ObjectMapper();
try {
this.json = mapper.readTree(jsonString);
} catch (Exception e) {
e.printStackTrace();
}
}
}
UPDATE:
If you mark jsonColumn with #Column spring will use reflection to pull out the data with default initialization null, getJsonColumn translation will never be executed:
#JsonIgnore
#Column(name = "json_column")
private String jsonColumn;
You do not need a jsonColumn, just make sure you mark your setters with #Column, so spring uses gettets/setters to persist to database, when persisting, jpa will execute getJsonColumn, when reading, jpa will execute setJsonColumn and jsonNode will be translated back and forward to string:
#Entity
#Table(name = "TryJson") public class TryJson {
private Integer id;
#Transient
private JsonNode jsonObjectHolder;
public TryJson() {
}
public TryJson(Integer id, String jsonColumn) {
this.id = id;
this.jsonObjectHolder = // use mapper to create the jsonObject;
}
#Id
#Column(name = "id")
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public JsonNode getJsonObjectHolder() {
return jsonObjectHolder;
}
public void setJsonObjectHolder(JsonNode jsonObjectHolder) {
this.jsonObjectHolder = jsonObjectHolder;
}
#Column(name = "json_column")
public String getJsonColumn() {
return this.jsonObjectHolder.toString();
}
public void setJsonColumn(String jsonColumn) throws IOException {
ObjectMapper mapper = new ObjectMapper();
this.jsonObjectHolder = mapper.readTree(jsonColumn);
}
#Override
public String toString() {
return String.format("TryJson [Id=%s, JsonColumn=%s, jsonObjectHolder=%s]", id, jsonColumn, jsonObjectHolder);
}
}
ObjectMapper mapper = new ObjectMapper();
TypeReference<List<User>> typeReference = new TypeReference<List<Your_Entity>>() {};
InputStream inputStream = TypeReference.class.getResourceAsStream("/bootstrap.json");
try {
List<Your_Entity> users = mapper.readValue(inputStream, typeReference);
log.info("Saving users...");
userService.saveAllUsers(users);
log.info(users.size() + " Users Saved...");
} catch (IOException e) {
log.error("Unable to save users: " + e.getMessage());
}
I am new to springboot, i am getting a response as below in my json response:
"Number": "08002050"
I have defined it as String in my spring boot app.
I want to get a response as below:
"Number": 08002050
How do i accomplish this. please help
You can manage it in server side with a tricky way.
public class User {
private int id;
private String name;
#JsonIgnore // ignore this field when serialize
private String number;
#JsonProperty(value = "number") // change name of field when serialize
private int intValueOfNumber;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getNumber() {
return number;
}
public void setNumber(String number) {
this.number = number;
}
public int getIntValueOfNumber() {
return Integer.parseInt(number); // parse number string to int
}
public void setIntValueOfNumber(int intValueOfNumber) {
this.intValueOfNumber = intValueOfNumber;
}
}
In this entity #JsonIgnore annotation is ignore your field for JSON serialization and pass intValueOfNumber as int to JSON. Your json will be following:
{"id":1,"name":"Java","number":44124}
You may lost zero suffix of number string when you parse it to int.
I am a running a project with SpringBoot. In this project I am calling an external Rest Service. I have modeled the response items into bean.
But when I get the response back the data are not serialised into the beans.
I guess there must be some configuration missing but I cannot find what.
I have added onfiguration spring-boot-starter-test to the configuration of Maven:
The rest client:
#RunWith(SpringRunner.class)
#SpringBootTest
public class RestClientTest {
#Autowired
private RestTemplateBuilder restTemplate;
#Test
public void sayHello() {
System.out.println("Hello");
assert(true);
}
#Test
public void testGetEmployee() {
RestTemplate template = restTemplate.build();;
HttpHeaders headers = new HttpHeaders();
List<MediaType> types = new ArrayList<MediaType>();
types.add(MediaType.APPLICATION_JSON);
types.add(MediaType.APPLICATION_XML);
headers.setAccept(types);
headers.set("Authorization", "Bearer gWRdGO7sUhAXHXBnjlBCtTP");
HttpEntity<Items> entity = new HttpEntity<Items>(headers);
String uri = "https://mytest.com/employees";
//ResponseEntity<String> rec = template.exchange(uri, HttpMethod.GET, entity, String.class);
//System.out.println("Received: " + rec);
ResponseEntity<Items> rec = template.exchange(uri, HttpMethod.GET, entity, Items.class);
System.out.println("Received: " + rec);
}
}
When I inspect the elements of the response it, I get a list, all the items are with null values
#JsonFormat(shape = JsonFormat.Shape.OBJECT)
public class Item implements Serializable {
#JsonProperty
private String id;
#JsonProperty
private String name;
#JsonProperty
private String email;
#JsonProperty
private String phone;
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
public String getPhone() {
return phone;
}
public void setPhone(String phone) {
this.phone = phone;
}
}
#JsonFormat(shape = JsonFormat.Shape.OBJECT)
public class Items implements Serializable {
#JsonProperty
private List<Item> items = new ArrayList<Item>();
public List<Item> getItems() {
return items;
}
}
Do you see what I am missing here?
The response is like this:
{
"items": [
{
"item": {
"id": 0,
"name": "string",
"email": "string",
"phone": "string",
Do you see what I am missing here?
Thanks
Gilles
The way you have implemented will try to deserialize data into Items class. But it doesn't have the required properties to deserialize. When you need to get a list of data through rest template exchange, you can get them as follows.
Get data as an array and convert it into arrayList.
Item[] itemArray = template.exchange(uri, HttpMethod.GET, entity, Item[].class).getBody();
List<Item> itemList = Arrays,asList(itemArray);
or
Use ParameterizedTypeReference to get data as a list
ResponseEntity<List<Item>> itemList = template.exchange(uri, HttpMethod.GET, entity, new ParameterizedTypeReference<List<Item>>() {});
List<Item> itemList = template.exchange(uri, HttpMethod.GET, entity, new ParameterizedTypeReference<List<Item>>() {}).getBody(); // access list directly
You might need to add this to your ObjectMapper:
mapper.configure(DeserializationFeature.UNWRAP_ROOT_VALUE, true);
And on your entity add #JsonRootName("item")
This is Json Object
[
{
"UserId":"demouser1",
"Catagories":[
{
"CatagoryName":"Entertainment",
"Persent":"25"
},
{
"CatagoryName":"Household",
"Persent":"25"
},
{
"CatagoryName":"Movie",
"Persent":"25"
},
{
"CatagoryName":"Misc",
"Persent":"25"
}
],
"RequestId":null,
"ResponseStatus":false,
"Token":null
}
]
Used The Following approach to parse the above Json
public class CategoryEntity {
private String CatagoryName;
private String Persent;
public String getCatagoryName() {
return CatagoryName;
}
public void setCatagoryName(String catagoryName) {
CatagoryName = catagoryName;
}
public String getPersent() {
return Persent;
}
public void setPersent(String persent) {
Persent = persent;
}
}
import java.util.List;
public class Entity {
private String UserId;
public String getUserId() {
return UserId;
}
public void setUserId(String userId) {
UserId = userId;
}
public List<CategoryEntity> getListCatagories() {
return ListCatagories;
}
public void setListCatagories(List<CategoryEntity> listPMMCatagories) {
ListCatagories = listPMMCatagories;
}
public String getRequestId() {
return RequestId;
}
public void setRequestId(String requestId) {
RequestId = requestId;
}
public boolean isResponseStatus() {
return ResponseStatus;
}
public void setResponseStatus(boolean responseStatus) {
ResponseStatus = responseStatus;
}
private List<CategoryEntity> ListCatagories;
private String RequestId;
private String Token;
public String getToken() {
return Token;
}
public void setToken(String token) {
Token = token;
}
private boolean ResponseStatus;
}
And
Following approach to convert the json object to corresponding object
Gson gson =new Gson();
JsonPrimitive listCatagoriesElement= element.getAsJsonPrimitive();
System.out.println("listCatagoriesElement.getAsString()>>"+listCatagoriesElement.getAsString());
sysout prints: listCatagoriesElement.getAsString()>>[{"UserId":"user1","ListCatagories":[{"CatagoryName":"Entertainment","Persent":"25"},{"CatagoryName":"Household","Persent":"25"},{"CatagoryName":"Movie","Persent":"25"},{"CatagoryName":"Misc","Persent":"25"}],"RequestId":null,"ResponseStatus":false,"Token":null}]
Entity entity = gson.fromJson(listCatagoriesElement, Entity.class);
Any ideas how should I fix it?
Thanks!
Your class CategoryEntity is correct, but in your class Entity, the attribute ListCatagories should be called Catagories to match the name in the JSON!
Apart from that, in order to parse the JSON you'd better do something like this:
Gson gson = new Gson();
Type listType = new TypeToken<List<Entity>>() {}.getType();
List<Entity> entities = gson.fromJson(yourJsonString, listType);
So you'll have a List containing just one Entity object, and you can access the values just with:
String catagoryNameI = entities.get(0).getCatagories().get(i).getCatagoryName();
String persentI = entities.get(0).getCatagories().get(i).getPersent();
You have to do this because your whole JSON response is an array, surrounded by [ ... ], so you need to parse it into some List...
I would like to unmarshal a json string to a pojo class.
I am reading it from an existing url:
https://builds.apache.org/job/Accumulo-1.5/api/json
I am using apache camel to unmarshal the url
#Component
public class RouteBuilder extends SpringRouteBuilder {
private Logger logger = LoggerFactory.getLogger(RouteBuilder.class);
#Override
public void configure() throws Exception {
logger.info("Configuring route");
//Properties die hij niet vindt in de klasse negeren
ObjectMapper objectMapper = new ObjectMapper();
objectMapper.configure(DeserializationConfig.Feature.FAIL_ON_UNKNOWN_PROPERTIES, false);
DataFormat reportFormat = new JacksonDataFormat(objectMapper, HealthReport.class);
from("timer://foo?fixedRate=true&delay=0&period=2000&repeatCount=1")
.routeId("accumoloToJsonRoute")
.setHeader(Exchange.HTTP_METHOD, constant("GET"))
.to("https://builds.apache.org:443/job/Accumulo-1.5/api/json")
.convertBodyTo(String.class)
.unmarshal(reportFormat) //instance van Build
.log(LoggingLevel.DEBUG, "be.kdg.teamf", "Project: ${body}")
.to("hibernate:be.kdg.teamf.model.HealthReport");
}
}
So far so good. I would like to only insert the 'healthReport' node using hibernate annotations.
#XmlRootElement(name = "healthReport")
#JsonRootName(value = "healthReport")
#Entity(name = "healthreport")
public class HealthReport implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private int Id;
#Column
#JsonProperty("description")
private String description;
#Column
#JsonProperty("iconUrl")
private String iconUrl;
#Column
#JsonProperty("score")
private int score;
public HealthReport() {
}
public HealthReport(int score, String iconUrl, String description) {
this.score = score;
this.iconUrl = iconUrl;
this.description = description;
}
public String getDescription() {
return description;
}
public String getIconUrl() {
return iconUrl;
}
public int getId() {
return Id;
}
public int getScore() {
return score;
}
public void setDescription(String description) {
this.description = description;
}
public void setIconUrl(String iconUrl) {
this.iconUrl = iconUrl;
}
public void setId(int id) {
Id = id;
}
public void setScore(int score) {
this.score = score;
}
}
This is where the problem is. It does not recognize the annotations
and only null values are inserted in my database
#XmlRootElement(name = "healthReport")
#JsonRootName(value = "healthReport")
Does anybody know how to fix this?
Thanks
Fixed it using a Processor for my Route
public class HealthReportProcessor implements Processor {
#Autowired
private ConfigurationService configurationService;
#Override
public void process(Exchange exchange) throws Exception {
ObjectMapper mapper = new ObjectMapper();
JsonNode root = mapper.readTree(exchange.getIn().getBody().toString());
ArrayNode report = (ArrayNode) root.get("healthReport");
int configId = configurationService.findJenkinsConfigurationByName(root.get("displayName").asText()).getId();
for (JsonNode node : report) {
JsonObject obj = new JsonObject();
obj.addProperty("description", node.get("description").asText());
obj.addProperty("iconUrl", node.get("iconUrl").asText());
obj.addProperty("score", node.get("score").asInt());
obj.addProperty("jenkinsConfig", configId);
exchange.getIn().setBody(obj.toString());
}
}
}
It is working but I think there is a better solution.
If you have a better solution please let me know ;)
Can you try this,
from("timer://foo?fixedRate=true&delay=0&period=2000&repeatCount=1")
.routeId("accumoloToJsonRoute")
.setHeader(Exchange.HTTP_METHOD,constant("GET"))
.to("https://builds.apache.org:443/job/Accumulo-1.5/apijson")
.unmarshal().json(JsonLibrary.Jackson, HealthReport.class)
And make sure the response params match the POJO fields.
Let me know if it works.