json deserialize field of internal object with jackson using data binding - json

I have a json like this
{
"name": "john",
"photo_urls":
{
"large": "http://www.server.com/john_photo.jpg"
}
}
and I would like to deserialize it in one class, like this
public class Person
{
String name;
String photoUrl;
}
instead of this
public class Person
{
String name;
public class photo_urls
{
String large;
}
}
Is it possible to do it with Jackson using Data Binding and the annotation #JsonProperty? Or is it necessary to use the streaming API instead?
Thanks for your help.

You can consider to have a setter method or a constructor parameter of the Map<String,String> type from which you can get the url field value. Here is an example:
public class JacksonDeserialize {
private static final String JSON = "{\n" +
" \"name\": \"john\",\n" +
" \"photo_urls\":\n" +
" {\n" +
" \"large\": \"http://www.server.com/john_photo.jpg\"\n" +
" }\n" +
"}";
public static class Person
{
private String name;
private String photoUrl;
#JsonCreator
public Person(#JsonProperty("name") String name, #JsonProperty("photo_urls") Map<String, String> photoUrl) {
this.name = name;
this.photoUrl = photoUrl.get("large");
}
#Override
public String toString() {
return "Person{" +
"photoUrl='" + photoUrl + '\'' +
", name='" + name + '\'' +
'}';
}
}
public static void main(String[] args) throws IOException {
ObjectMapper mapper = new ObjectMapper();
System.out.println(mapper.readValue(JSON, Person.class));
}
}
Output:
Person{photoUrl='http://www.server.com/john_photo.jpg', name='john'}

Related

Convert JSON String to JSON

I am querying Postgres DB for retrieving JSONB format data using spring data JPA, which is stored like this in DB :
"{
"name":"abc",
"place"="xyz"
}"
but I am getting the response back with out double quotes, is there a way that I get the double quotes using spring data JPA or convert back to JSON with double quotes?
I tried new GSON().tJson() and ObjectMapper but no luck any help is appreciated.
Thanks
Your JSON object format is quite good and you should have no issue deserializing it back to a POJO.
Assuming your POJO class is named Template, here down a sample de-serialization implementation:
public class SerializationTest {
private static final String OBJECT = "{\n"
+ "\"name\":\"abc\",\n"+
"\"place\"=\"xyz\"\n"+
"}";
public static void main(String[] args) {
System.out.println("Serialized object:\n" + OBJECT);
Gson gson = new Gson();
Template template = gson.fromJson(OBJECT, Template.class);
System.out.println(template);
}
private static class Template {
private String name;
private String place;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getPlace() {
return place;
}
public void setPlace(String place) {
this.place = place;
}
#Override
public String toString() {
return "Template{" +
"name='" + name + '\'' +
", place='" + place + '\'' +
'}';
}
}
}

Unable to Deserialize a simple enum

This is my class User
#JsonTypeInfo(use = JsonTypeInfo.Id.NAME, include = JsonTypeInfo.As.EXISTING_PROPERTY, property = "userType")
#JsonSubTypes({
#JsonSubTypes.Type(value = Broker.class, name = "BROKER"),
#JsonSubTypes.Type(value = Admin.class, name = "ADMIN")
})
public class User {
protected String password;
#Id
protected String username;
protected boolean accountNonExpired=true, accountNonBlocked=true, enabled=true, credentialNonExpired=true;
protected String[] grantedAuthorities;
protected UserType userType;
public User(){
}
public UserType getUserType() {
return userType;
}
public void setUserType(UserType userType) {
this.userType = userType;
}
#Override
public String toString() {
return "User{" +
"password='" + password + '\'' +
", username='" + username + '\'' +
", accountNonExpired=" + accountNonExpired +
", accountNonBlocked=" + accountNonBlocked +
", enabled=" + enabled +
", credentialNonExpired=" + credentialNonExpired +
", grantedAuthorities=" + Arrays.toString(grantedAuthorities) +
", userType=" + userType +
'}';
}
It uses an enum as a field of type `UserType
public enum UserType {
BROKER("BROKER"),ADMIN("ADMIN");
private static Map<String, UserType> FORMAT_MAP = Stream
.of(UserType.values())
.collect(Collectors.toMap(s -> s.value, Function.identity()));
private final String value;
UserType(String value) {
this.value = value;
}
#JsonCreator
public static UserType fromString(String string) {
return Optional
.ofNullable(FORMAT_MAP.get(string))
.orElseThrow(() -> new IllegalArgumentException(string));
}
#JsonValue
public String getValue(){
return value;
}
}
But ObjectMapper is not able to deserialize the UserType enum
My Json String :
{"password":"135","username":"cooper","accountNonExpired":true,"grantedAuthorities":["ROLE_BROKER"],"userType":"BROKER","brokerName":null,"accountNonLocked":true,"credentialsNonExpired":true,"enabled":true}
This is the code to deserialze :
User user = objectMapper.readValue(jsonString,User.class);
But after desierializing to User object, this is how the user object looks :
{password='135', username='cooper', accountNonExpired=true, accountNonBlocked=true, enabled=true, credentialNonExpired=true, grantedAuthorities=[ROLE_BROKER], userType=null}
As you can see, userType is null. What is going wrong?
It works after I changed the include = JsonTypeInfo.As.EXISTING_PROPERTY to include = JsonTypeInfo.As.PROPERTY.

JAX-RS/Jackson -- Deserialize JSON with Unknown Root Element Name?

I am writing a RESTeasy Proxy Client to consume Apple's API for retrieving their iTunes category list. When you query for information about a given category , for example with this URL:
https://itunes.apple.com/WebObjects/MZStoreServices.woa/ws/genres?id=1420
...you get a JSON response that looks like this:
{
"1420":{
"name":"Self-Help",
"id":"1420",
"url":"https://itunes.apple.com/us/genre/podcasts-health-self-help/id1420?mt=2",
"rssUrls":{
"topVideoPodcastEpisodes":"https://itunes.apple.com/us/rss/topvideopodcastepisodes/genre=1420/json",
"topAudioPodcasts":"https://itunes.apple.com/us/rss/topaudiopodcasts/genre=1420/json",
"topVideoPodcasts":"https://itunes.apple.com/us/rss/topvideopodcasts/genre=1420/json",
"topPodcasts":"https://itunes.apple.com/us/rss/toppodcasts/genre=1420/json",
"topAudioPodcastEpisodes":"https://itunes.apple.com/us/rss/topaudiopodcastepisodes/genre=1420/json",
"topPodcastEpisodes":"https://itunes.apple.com/us/rss/toppodcastepisodes/genre=1420/json"
},
"chartUrls":{
"videoPodcastEpisodes":"https://itunes.apple.com/WebObjects/MZStoreServices.woa/ws/charts?cc=us&g=1420&name=VideoPodcastEpisodes",
"podcasts":"https://itunes.apple.com/WebObjects/MZStoreServices.woa/ws/charts?cc=us&g=1420&name=Podcasts",
"audioPodcastEpisodes":"https://itunes.apple.com/WebObjects/MZStoreServices.woa/ws/charts?cc=us&g=1420&name=AudioPodcastEpisodes",
"audioPodcasts":"https://itunes.apple.com/WebObjects/MZStoreServices.woa/ws/charts?cc=us&g=1420&name=AudioPodcasts",
"podcastEpisodes":"https://itunes.apple.com/WebObjects/MZStoreServices.woa/ws/charts?cc=us&g=1420&name=PodcastEpisodes",
"videoPodcasts":"https://itunes.apple.com/WebObjects/MZStoreServices.woa/ws/charts?cc=us&g=1420&name=VideoPodcasts"
}
}
}
I am trying to map this JSON response to a Java object using JAXB and Jackson. However, the "1420" root element name seems to be causing a problem, as I get the following exception when calling my client:
Unrecognized field "1420" (class foo.bar.ITunesCategoryList), not marked as ignorable
My JAXB class looks like this:
#XmlRootElement
#XmlAccessorType(XmlAccessType.FIELD)
public class ITunesCategory implements TransferObject {
private static final long serialVersionUID = 3443545925023804457L;
#XmlElement(name = "id")
#JsonProperty("id")
private String identifier = null;
#XmlElement
private String name = null;
#XmlElementWrapper(name = "subgenres")
private List<ITunesCategory> subcategories = new ArrayList<ITunesCategory>();
...
}
I've even tried creating a wrapper class since the search could result in more than one category being returned. It looks like this:
#XmlRootElement
#XmlAccessorType(XmlAccessType.FIELD)
public class ITunesCategoryList implements TransferObject {
private static final long serialVersionUID = 3303125979016445238L;
#XmlElement
private List<ITunesCategory> categories = new ArrayList<ITunesCategory>();
...
}
However, regardless of which class I specify as my return type, I get the same error because the category identifier is the root element name of the JSON object.
Is there any way to tell JAXB/Jackson/JAX-RS/RESTeasy to ignore the root element name and just map the underlying object to Java? There is no way for me to know the root element name at develop/compile time, since it corresponds directly to the results returned by the search. Is there anything that can be done to get around this exception? Thanks for any help you can give!
I couldn't find much on dynamically ignoring the root, at least not anything that would be suitable in a JAX-RS environment. The only thing I could think is to write a custom deserializer, and just skip the root node. Something like
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.DeserializationContext;
import com.fasterxml.jackson.databind.JsonDeserializer;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import java.io.IOException;
import java.util.Map;
public abstract class IHateRootElemsJsonDeserializer<T> extends JsonDeserializer<T> {
private final ObjectMapper mapper = new ObjectMapper();
private final Class<T> cls;
public IHateRootElemsJsonDeserializer(Class<T> cls) {
this.cls = cls;
}
#Override
public T deserialize(JsonParser jp, DeserializationContext dc)
throws IOException, JsonProcessingException {
JsonNode rootNode = jp.getCodec().readTree(jp);
Map.Entry<String,JsonNode> field = rootNode.fields().next();
JsonNode node = field.getValue();
T result = mapper.convertValue(node, cls);
return result;
}
}
Then just extend it with a concrete type.
public class GenreDeserializer extends IHateRootElemsJsonDeserializer<Genre>{
public GenreDeserializer() {
super(Genre.class);
}
}
Here's a test using the exact JSON you provided above
public class Test {
public static void main(String[] args) throws Exception {
ObjectMapper mapper = new ObjectMapper();
GenreDeserializer deserializer = new GenreDeserializer();
SimpleModule module = new SimpleModule();
module.addDeserializer(Genre.class, deserializer);
mapper.registerModule(module);
Genre genre = mapper.readValue(JSON_FILE, Genre.class);
System.out.println(genre);
genre = mapper.readValue(JSON_FILE, Genre.class);
System.out.println(genre);
}
static final File JSON_FILE = new File("json.json");
}
The model
public class Genre {
public String id;
public String name;
public String url;
public RssUrls rssUrls;
public ChartUrls chartUrls;
#Override
public String toString() {
return "Category{" + "id=" + id + ", name="
+ name + ", url=" + url + ", rssUrls=" + rssUrls + '}';
}
public static class RssUrls {
public String topVideoPodcastEpisodes;
public String topAudioPodcasts;
public String topVideoPodcasts;
public String topPodcasts;
public String topAudioPodcastEpisodes;
public String topPodcastEpisodes;
#Override
public String toString() {
return "RssUrls{" + "topVideoPodcastEpisodes=" + topVideoPodcastEpisodes
+ ", topAudioPodcasts=" + topAudioPodcasts
+ ", topVideoPodcasts=" + topVideoPodcasts
+ ", topPodcasts=" + topPodcasts
+ ", topAudioPodcastEpisodes=" + topAudioPodcastEpisodes
+ ", topPodcastEpisodes=" + topPodcastEpisodes + '}';
}
}
public static class ChartUrls {
public String videoPodcastEpisodes;
public String podcasts;
public String audioPodcastEpisodes;
public String audioPodcasts;
public String podcastEpisodes;
public String videoPodcasts;
#Override
public String toString() {
return "ChatUrls{" + "videoPodcastEpisodes=" + videoPodcastEpisodes
+ ", podcasts=" + podcasts
+ ", audioPodcastEpisodes=" + audioPodcastEpisodes
+ ", audioPodcasts=" + audioPodcasts
+ ", podcastEpisodes=" + podcastEpisodes
+ ", videoPodcasts=" + videoPodcasts + '}';
}
}
}
To configure the ObjectMapper in JAX-RS, you can have a look at this post

How do I deserialize this JSON using Gson?

I am a bit lost here,
I have a JSON string like this:
{
"type":"fuu",
"message":"bar",
"data":{
"5":{
"post":"foo",
"type":"bar",
},
"0":{
"post":"foo",
"type":"bar",
},
"1":{
"post":"foo",
"type":"bar",
},
// and so on...
}
}
Please how do I parse it into POJOs using Gson? (I need to get the list of objects)
I am a bit confused by the number in front of the elements of the list of objects....
Try this -
Pojo.java
import java.util.Map;
public class Pojo {
private String type;
private String message;
private Map<Integer, InnerPojo> data;
public String getType() {
return type;
}
public void setType(String type) {
this.type = type;
}
public String getMessage() {
return message;
}
public void setMessage(String message) {
this.message = message;
}
public Map<Integer, InnerPojo> getData() {
return data;
}
public void setData(Map<Integer, InnerPojo> data) {
this.data = data;
}
#Override
public String toString() {
return "Pojo [type=" + type + ", message=" + message + ", data=" + data
+ "]";
}
}
InnerPojo.java
public class InnerPojo {
private String type;
private String post;
public String getType() {
return type;
}
public void setType(String type) {
this.type = type;
}
public String getPost() {
return post;
}
public void setPost(String post) {
this.post = post;
}
#Override
public String toString() {
return "InnerPojo [type=" + type + ", post=" + post + "]";
}
}
Main.java
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.testgson.beans.Pojo;
public class Main {
private static Gson gson;
static {
gson = new GsonBuilder().create();
}
public static void main(String[] args) {
String j = "{\"type\": \"fuu\", \"message\": \"bar\", \"data\":{ \"0\":{\"post\": \"foo\", \"type\": \"bar\"}, \"1\":{\"post\": \"foo\", \"type\": \"bar\"}, \"5\":{\"post\": \"foo\", \"type\": \"bar\"}}}";
Pojo p = gson.fromJson(j, Pojo.class);
System.out.println(p);
}
}
And Result is -
Pojo [type=fuu, message=bar, data={0=InnerPojo [type=bar, post=foo], 1=InnerPojo [type=bar, post=foo], 5=InnerPojo [type=bar, post=foo]}]
For the "data" part, I'd try to parse it into a Map<Integer, TypedPost> structure, see this thread for instructions.

Deserializing JSON into typed class

I have a base class and two sublcasses:
public /*abstract*/ class TargetPoi {
private String poiId;
...
}
public class TargetSub1Poi extends TargetPoi {
private String oneMoreId;
...
}
public class TargetSub2Poi extends TargetPoi {
...
}
Is it possible to declare the base class abstract? ...I always get an exception when a JSON is send with the request if I use the abstract keyword...
Exception Description: This class does not define a public default constructor, or the constructor raised an exception.
Internal Exception: java.lang.InstantiationException
Descriptor: XMLDescriptor(com.foo.bar.TargetPoi --> [])
When the POST request with its JSON in the request body is coming into the Jersy Resource I want to deserialize the JSON into the proper TargetPoi subclasses.
The JSON:
{
"requestId": "84137f1ab38f4bf585d13984fc07c621",
"startTime": "2013-10-30T18:30:00+02:00",
"endTime": "2013-10-30T18:45:00+02:00",
"targetPoi":
{
"poiId": "0000000602",
"oneMoreId": "1"
},
"type": "Block",
"notification": true
}
My Resource hast a method defined this way...
#POST
#Produces(MediaType.APPLICATION_JSON)
#Consumes(MediaType.APPLICATION_JSON)
public Response doReservation(final ReservationDO reservationDO
, #QueryParam("serviceType") final String serviceType) {
...
}
The JSON shall be deserialized in this class:
#XmlRootElement
public class ReservationDO<T extends TargetPoi>
{
public String requestId;
public String startTime;
public String endTime;
public String serviceType;
public T targetPoi;
...
}
How can I tell Jackson to bind the JSON for the targetPoi properly to the correct subtype (TargetSub1Poi)? The serviceType could tell me to which subtype the targetPoi is to bind to...but I think this information can't be used from Jackson, does it? When I print out the deserialized JSON in th edoreservation method the oneMoreId part coming with the original JSON is lost.
Do I have to provide any TypeInfo or can I achieve it without?
It is possible to declare your parent class abstract and have a generic field. You just need to tell Jackson which sub-type to use to create an instance of you object. It can be done by using #JsonTypeInfo annotation as described at the Jackson polymorphic deserialization wiki page.
Here is an example:
public class JacksonPoi {
#JsonTypeInfo(use = JsonTypeInfo.Id.NAME,
include = JsonTypeInfo.As.PROPERTY, property = "type")
public static abstract class TargetPoi {
public String poiId;
}
#JsonTypeName("sub1")
public static class TargetSub1Poi extends TargetPoi {
public String oneMoreId;
#Override
public String toString() {
return "TargetSub1Poi{" +
"poiId='" + poiId + '\'' +
"oneMoreId='" + oneMoreId + '\'' +
'}';
}
}
#JsonTypeName("sub2")
public static class TargetSub2Poi extends TargetPoi {
public String twoMoreId;
#Override
public String toString() {
return "TargetSub2Poi{" +
"poiId='" + poiId + '\'' +
"twoMoreId='" + twoMoreId + '\'' +
'}';
}
}
public static class Bean<T extends TargetPoi> {
public String field;
public T poi;
#Override
public String toString() {
return "Bean{" +
"field='" + field + '\'' +
", poi=" + poi +
'}';
}
}
public static final String JSON = "{\n" +
"\"poi\": \n" +
"{\n" +
" \"type\": \"sub1\",\n" +
" \"poiId\": \"0000000602\",\n" +
" \"oneMoreId\": \"1\"\n" +
"},\n" +
"\"field\": \"value1\"\n" +
"}";
public static final String JSON2 = "{\n" +
"\"poi\": \n" +
"{\n" +
" \"type\": \"sub2\",\n" +
" \"poiId\": \"0000000602\",\n" +
" \"twoMoreId\": \"13\"\n" +
"},\n" +
"\"field\": \"value2\"\n" +
"}";
public static void main(String[] args) throws IOException {
ObjectMapper mapper = new ObjectMapper();
mapper.registerSubtypes(TargetSub1Poi.class, TargetSub2Poi.class);
Bean<TargetSub1Poi> poi1 = mapper.readValue(JSON, new TypeReference<Bean<TargetSub1Poi>>() {});
System.out.println(poi1);
Bean<TargetSub2Poi> poi2 = mapper.readValue(JSON2, new TypeReference<Bean<TargetSub2Poi>>() {});
System.out.println(poi2);
}
}
Output:
Bean{field='value1', poi=TargetSub1Poi{poiId='0000000602'oneMoreId='1'}}
Bean{field='value2', poi=TargetSub2Poi{poiId='0000000602'twoMoreId='13'}}