i don't know what or if i'm doing it wrong, but the conversion from json to my POJOs just don't work. I'm with the dependencies updated. When i called the method "readJSONFromURL" my return always is the object with yours attributes with value NULL. All the libs are up to date. My Objects:
public class ListHall {
private List<Hall> Hall;
public void setHall(List<Hall> hall) {
this.Hall = hall;
}
public List<Hall> getHall() {
if (Hall == null) {
this.Hall = new ArrayList<Hall>();
}
return Hall;
}
}
public class Hall {
private String id;
private String name;
private Integer count;
//getter and setters here
}
My DataMappers:
data_mapper ListHall:ListHallMapper
data_mapper Hall:HallMapper
My method conversor:
public ListHall mapHall() throws IOException {
ListHallMapper returnListHallMapper = new ListHallMapper();
HallMapper hallMapper = new HallMapper();
DataMapper.createContext(Arrays.asList(returnListHallMapper,
hallMapper),
(DataMapper mapper) -> {
mapper.setReadKeyConversions(Arrays.asList(DataMapper.CONVERSION_CAMEL_TO_SNAKE));
});
return returnListHallMapper.readJSONFromURL("http://192.168.0.16:7080/integration/hall", ListHall.class,"/ListHall");
}
My output JSON:
{"ListHall":{"Hall":[{"id":"0","name":"Salão 1","count":"10"},{"id":"f6a495c1-be5e-4476-a362-5d42e572bfae","name":"Salão 3","count":"0"}]}}
My result:My LIB:
What i'm doing it wrong?
Related
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;
}
I have a class which I'm trying to deserialise some JSON into. One of the properties has a custom converter attached via the #JsonDeserialize annotation. The deserialisation works fine when the class is written with setters and the annotations on the setters like this:
public class Rates extends AbstractResponse {
private Date effectiveDate;
private List<Rate> rates;
public Rates(Date effectiveDate, List<Rate> rates) {
this.effectiveDate = effectiveDate;
this.rates = rates;
}
public Date getEffectiveDate() {
return this.effectiveDate;
}
#JsonProperty("timestamp")
public void setEffectiveDate(Date effectiveDate) {
this.effectiveDate = effectiveDate;
}
#JsonDeserialize(converter = RatesTableConverter.class)
public void setRates(List<Rate> rates) {
this.rates = rates;
}
public List<Rate> getRates() {
return this.rates;
}
}
But when I re-write it to be like this:
public class Rates extends AbstractResponse {
private final Date effectiveDate;
private final List<Rate> rates;
#JsonCreator
public Rates(
#JsonProperty("timestamp") Date effectiveDate,
#JsonProperty("rates")
#JsonDeserialize(converter = RatesTableConverter.class) List<Rate> rates) {
this.effectiveDate = effectiveDate;
this.rates = rates;
}
public Date getEffectiveDate() {
return this.effectiveDate;
}
public List<Rate> getRates() {
return this.rates;
}
}
I get this error:
com.fasterxml.jackson.databind.JsonMappingException: Can not deserialize instance of java.util.ArrayList out of START_OBJECT token
I've messed around with a number of ideas and cannot get it to work. It appears that when used on a constructor, that the deserialiser is not being checked correctly.
Any ideas what I've missed?
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've created a set of classes (pojos) that need to be transformed into json. because i have a constraint that json field names adhere to a certain format, i've settled on gson as my library of choice, as it allows for annotations of field names.
so, i have json field names like asset_type, preview_image_thumbnail, etc. along with that, any metadata fields must have the format, metadata:<metadata-field-name>.
so, what this comes down to is that my metadata:tags and metadata:site annotations will not be transformed by gson, since they are not valid json field names, according to gson, at least.
all works well, except for those darned metadata field names. my goal is to have output like the following:
{
"name": "Test Remote Asset",
"description": "test-remote-asset",
"asset_type": "remote_asset",
"duration": 172360,
"stream_urls": {
"flash": "http://www.test-site.com/videos/a-video.flv",
"iphone": "http://www.test-site.com/videos/a-video.3gp",
"ipad": "http://www.test-site.com/videos/a-video.3gp",
"source_file": "http://www.test-site.com/videos/a-video.mp4"
},
"metadata:tags": "tag1,tag2,tag3",
"metadata:site": "test-site"
}
here is the exception i get when attempting to transform my class to json:
java.lang.IllegalArgumentException: metadata:tags is not a valid JSON field name.
and here is the class i want to transform:
public class RemoteAsset {
/** The video's name **/
private String name;
/** The video's description **/
private String description;
/** The video asset type **/
#SerializedName("asset_type")
private String assetType;
/** The video's duration, in milliseconds **/
private long duration;
/** The video's thumbnail preview URL **/
#SerializedName("preview_image_url")
private String previewImageUrl;
/** The video's OpenCms Structure ID **/
#SerializedName("external_id")
private String externalId;
/** The video's various streaming URLs **/
#SerializedName("stream_urls")
private StreamUrls streamUrls;
/** The video's tags, coma-separated **/
#SerializedName("metadata:tags")
private String metadataTags;
/** The video's host site **/
#SerializedName("metadata:site")
private String metadataSite;
public String getMetadataTags() {
return metadataTags;
}
public void setMetadataTags(String metadata_tags) {
this.metadataTags = metadata_tags;
}
public String getMetadataSite() {
return metadataSite;
}
public void setMetadataSite(String metadata_site) {
this.metadataSite = metadata_site;
}
public RemoteAsset() {
this.streamUrls = null;
this.assetType = null;
this.previewImageUrl = "";
this.metadataSite = "";
this.metadataTags = "";
this.externalId = "";
this.description = "";
this.duration = 0L;
this.name = "";
}
public String getName() {
return this.name;
}
public void setName(String name) {
this.name = name;
}
public String getDescription() {
return this.description;
}
public void setDescription(String description) {
this.description = description;
}
public String getAssetType() {
return this.assetType;
}
public void setAssetType(ASSET_TYPE asset_type) {
this.assetType = asset_type.getTypeName();
}
public long getDuration() {
return this.duration;
}
public void setDuration(long duration) {
this.duration = duration;
}
public String getPreviewImageUrl() {
return this.previewImageUrl;
}
public void setPreviewImageUrl(String preview_image_url) {
this.previewImageUrl = preview_image_url;
}
public String getExternalId() {
return this.externalId;
}
public void setExternalId(String external_id) {
this.externalId = external_id;
}
public StreamUrls getStreamUrls() {
return this.streamUrls;
}
public void setStreamUrls(StreamUrls stream_urls) {
this.streamUrls = stream_urls;
}
#Override
public String toString() {
StringBuilder builder = new StringBuilder();
builder.append("RemoteAsset [name=").append(this.name)
.append(", description=").append(this.description)
.append(", assetType=").append(this.assetType)
.append(", duration=").append(this.duration)
.append(", previewImageUrl=").append(this.previewImageUrl)
.append(", externalId=").append(this.externalId)
.append(", streamUrls=").append(this.streamUrls).append("]");
return builder.toString();
}
}
The problem is that those can't be mapped directly to Java variables because you can't have a colon in a variable name. You need to use the Gson #SerializedName annotation. The following works at least in Gson version 2.2.2:
public static void main( String[] args )
{
String json = "{\"some:field\":\"foo\"}";
Gson gson = new Gson();
MyClass mc = gson.fromJson(json, MyClass.class);
json = gson.toJson(mc);
System.out.println(json);
}
class MyClass
{
// String some:field; <- You can do that!
#SerializedName("some:field")
String someField;
}
Output:
{"some:field":"foo"}
So my entities look like this:
public class HappyClass<T>
{
private String id;
prviate int ver;
private Object obj;
public String getId()
{
return this.id;
}
public void setId( String id )
{
this.id = id;
}
public int getVer()
{
return this.ver;
}
public void setVer( int ver )
{
this.ver = ver;
}
#JsonTypeInfo( use = Id.NONE )
public T getObj()
{
return obj;
}
public void setObj( T obj )
{
this.obj = obj;
}
}
public class HappyGeneric
{
private String someStuff();
public String getSomeStuff()
{
return this.someStuff();
}
public void setSomeStuff( String someStuff )
{
this.someStuff = someStuff;
}
}
If I instantiate a class like this:
HappyClass<HappyGeneric> hc = new HappyClass<HappyGeneric>();
If I send it to Spring in a #ResponseBody it returns this:
{
"id" : "iamsomeid",
"ver" : 123,
"obj" : {
"someStuff" : "iamsomestuff"
}
}
However, when Spring and/or Jackson attempts to unmarshal the same JSON, it figures out that the main class is a HappyClass, however, the getObj() it unmarshals to a LinkedHashMap and not a HappyGeneric no matter what I seem to annotate it with.
Anybody have any ideas how I can force Jackson to unmarshal that generic to the original class?
Thanks!
EDIT: I'm aware I can call mapper.convertValue( blah.getObj(), HappyGeneric.class ) and get the object out that way-- I was hoping to get Spring to figure it out automatically (through annotations, for example).