I've been trying to upgrade the JSON modules to use the FasterXML (2.6.3) versions of Jackson instead of the old Codehaus modules. During the upgrade, I've noticed that the naming strategy differs when using FasterXML instead of Codehaus.
Codehaus was more flexible when it came to the naming strategy. The test below highlights the issue I'm facing with FasterXML. How can I configure the ObjectMapper so it follows the same strategy like Codehaus?
I cannot alter the JSONProperty annotations as there are hundreds of them. I would like the upgrade to be backwards compatible with respect to the naming strategy.
import java.io.IOException;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.PropertyNamingStrategy;
/*import org.codehaus.jackson.annotate.JsonIgnoreProperties;
import org.codehaus.jackson.annotate.JsonProperty;
import org.codehaus.jackson.map.ObjectMapper;
import org.codehaus.jackson.map.PropertyNamingStrategy;*/
import org.junit.Assert;
import org.junit.Test;
public class JSONTest extends Assert {
#JsonIgnoreProperties(ignoreUnknown = true)
public static class Product {
#JsonProperty(value = "variationId")
private String variantId;
#JsonProperty(value = "price_text")
private String priceText;
#JsonProperty(value = "listPrice")
public String listPrice;
#JsonProperty(value = "PRODUCT_NAME")
public String name;
#JsonProperty(value = "Product_Desc")
public String description;
}
private static final String VALID_PRODUCT_JSON =
"{ \"list_price\": 289," +
" \"price_text\": \"269.00\"," +
" \"variation_id\": \"EUR\"," +
" \"product_name\": \"Product\"," +
" \"product_desc\": \"Test\"" +
"}";
#Test
public void testDeserialization() throws IOException {
ObjectMapper mapper = new ObjectMapper();
mapper.setPropertyNamingStrategy(PropertyNamingStrategy.CAMEL_CASE_TO_LOWER_CASE_WITH_UNDERSCORES);
Product product = mapper.readValue(VALID_PRODUCT_JSON, Product.class);
System.out.println(mapper.writerWithDefaultPrettyPrinter().writeValueAsString(product));
assertNotNull(product.listPrice);
assertNotNull(product.variantId);
assertNotNull(product.priceText);
assertNotNull(product.name);
assertNotNull(product.description);
}
}
#JsonProperty overrides any PropertyNamingStrategy in fasterxml since version 2.4.0. However, yet-to-be-released version 2.7.0 will provide a feature to allow you to opt back in to the old behavior. There is also an unimplemented suggestion to toggle this at the per-annotation level, but that would not really help you.
It appears that Codehaus does apply the PropertyNamingStrategy on top of the #JsonProperty values when mapping, although I can't find any clear docs on that. This appears to have been the behavior in fasterxml before 2.4.0 as well. Here is another example of someone noticing the same difference in behavior.
Although the solution provided by SkinnyJ is perfect for your problem, but if you can't wait till 2.7 is released, you can apply the below hack to get around the problem.
The idea is to transform the incoming JSON to match the attributes in your bean definition. Below code does that. Following points should be noted:
If you are dealing with nested structures, you will have to implement a recursive function to achieve this transformation.
There is a little overhead involved in doing the transformation.
Code:
public class JSONTest extends Assert {
#JsonIgnoreProperties(ignoreUnknown = true)
public static class Product {
#JsonProperty(value = "variationId")
private String variantId;
#JsonProperty(value = "price_text")
private String priceText;
#JsonProperty(value = "listPrice")
public String listPrice;
#JsonProperty(value = "PRODUCT_NAME")
public String name;
#JsonProperty(value = "Product_Desc")
public String description;
}
private static final String VALID_PRODUCT_JSON =
"{ \"list_price\": 289," +
" \"price_text\": \"269.00\"," +
" \"variation_id\": \"EUR\"," +
" \"product_name\": \"Product\"," +
" \"product_desc\": \"Test\"" +
"}";
#Test
public void testDeserialization() throws IOException {
ObjectMapper mapper = new ObjectMapper();
mapper.setPropertyNamingStrategy(PropertyNamingStrategy.CAMEL_CASE_TO_LOWER_CASE_WITH_UNDERSCORES);
//Capture the original JSON in org.json.JSONObject
JSONObject obj = new JSONObject(VALID_PRODUCT_JSON);
JSONArray keys = obj.names();
//New json object to be created using property names defined in bean
JSONObject matchingJson = new JSONObject();
//Map of lowercased key to original keys in incoming json. eg: Prod_id > prodid
Map<String, String> jsonMappings = new LinkedHashMap<String, String>();
for (int i = 0; i < keys.length(); i++) {
String key = lowerCaseWithoutUnderScore(keys.getString(i));
String value = keys.getString(i);
jsonMappings.put(key, value);
}
/*
* Iternate all jsonproperty beans and create new json
* such that keys in json map to that defined in bean
*/
Field[] fields = Product.class.getDeclaredFields();
for (Field field : fields) {
JsonProperty prop = field.getAnnotation(JsonProperty.class);
String propNameInBean = prop.value();
String keyToLook = lowerCaseWithoutUnderScore(propNameInBean);
String keyInJson = jsonMappings.get(keyToLook);
matchingJson.put(propNameInBean, obj.get(keyInJson));
}
String json = matchingJson.toString();
System.out.println(json);
//Pass the matching json to Object mapper
Product product = mapper.readValue(json, Product.class);
System.out.println(mapper.writerWithDefaultPrettyPrinter().writeValueAsString(product));
assertNotNull(product.listPrice);
assertNotNull(product.variantId);
assertNotNull(product.priceText);
assertNotNull(product.name);
assertNotNull(product.description);
}
private String lowerCaseWithoutUnderScore(String key){
return key.replaceAll("_", "").toLowerCase();
}
}
Related
I have the answer to my own question, so I post both the answer and solution, as explicitly encouraged by Jeff Atwood. My question was originally for Kotlin, but while trying to find a solution, I also tried Java, so I provide the question and solution in both Java and Kotlin.)
Question in Kotlin
Given this deserializable Product class:
data class Product(val name: String, val prices: List<Int>)
and this json string that lacks the prices field:
{"name": "Computer"}
how can I deserialize the json string to a Product object using Jackson?
What I have tried in Kotlin
I tried this:
data class Product(val name: String, val prices: List<Int>)
// Missing "prices" field
val json = """{"name": "Computer"}"""
// "prices" field included works fine
// val json = """{"name": "Computer", "prices": [1,2,3]}"""
val mapper = ObjectMapper().registerKotlinModule()
val product = mapper.readValue<Product>(json)
println(product)
but it results in this exception:
com.fasterxml.jackson.module.kotlin.MissingKotlinParameterException: Instantiation of
[simple type, class MainKt$main$Product] value failed for JSON property prices due to
missing (therefore NULL) value for creator parameter prices which is a non-nullable type
at [Source: (String)"{"name": "Computer"}"; line: 1, column: 20]
(through reference chain: MainKt$main$Product["prices"])
When using Java
For Java the Product class would be:
class Product {
private String name;
private List<Integer> prices;
public Product(String name, List<Integer> prices) {
this.name = name;
this.prices = prices;
}
#Override
public String toString() {
return "Product{name='" + name + "\", prices=" + prices + '}';
}
}
with this Jackson code:
String json = "{\"name\": \"Computer\"}";
// String json = "{\"name\": \"Computer\", \"prices\": [1,2,3]}";
ObjectMapper mapper = new ObjectMapper();
// ParameterNamesModule is needed for non-arg constructor when not using Jackson annotations
mapper.registerModule(new ParameterNamesModule());
Product product = mapper.readValue(json, Product.class);
// Shows "prices=null", while "prices=[]" is required
System.out.println(product);
But this sets prices to null instead of an empty list.
Solution in Kotlin
This solution is for Jackson 2.11 and higher. It uses the jackson-module-kotlin Maven artifact.
val kotlinModule = KotlinModule.Builder()
.configure(KotlinFeature.NullToEmptyCollection, true)
.build()
val mapper = ObjectMapper().registerModule(kotlinModule)
val product = mapper.readValue(json, Product::class.java)
println(product)
So the solution uses KotlinFeature.NullToEmptyCollection, which has the following documentation:
Default: false. Whether to deserialize null values for collection
properties as empty collections.
There is also a map version: KotlinFeature.NullToEmptyMap.
For version 2.9 and 2.10 you can use the nullToEmptyCollection default parameter of the KotlinModule constructor.
Solution in Java using annotations
Annotated Product class:
class Product {
private String name;
private List<Integer> prices;
public Product(#JsonProperty("name") String name,
#JsonProperty("prices")
#JsonSetter(nulls = Nulls.AS_EMPTY) List<Integer> prices
) {
this.name = name;
this.prices = prices;
}
#Override
public String toString() {
return "Product{name='" + name + "\', prices=" + prices + '}';
}
}
Jackson code:
String json = "{\"name\": \"Computer\"}";
ObjectMapper mapper = new ObjectMapper();
Product product = mapper.readValue(json, Product.class);
System.out.println(product); // Product{name='Computer', prices=[]}
The key part in this solution is #JsonSetter(nulls = Nulls.AS_EMPTY), which sets the missing or null json field to an empty list in Java.
The number of verbose annotations, such as #JsonProperty("prices") can be reduced by using the jackson-module-parameter-names Maven artifact. Then only #JsonSetter(nulls = Nulls.AS_EMPTY) is needed.
Solution in Java without annotations
This solution requires the jackson-module-parameter-names Maven artifact. When using this module/artifact, don't forget to add the -parameters compiler argument.
Product class Jackson without annotations:
class Product {
private String name;
private List<Integer> prices;
public Product(String name, List<Integer> prices) {
this.name = name;
this.prices = prices;
}
#Override
public String toString() {
return "Product{name='" + name + "\", prices=" + prices + '}';
}
}
Jackson code:
String json = "{\"name\": \"Computer\"}";
ObjectMapper mapper = new ObjectMapper();
mapper.registerModule(new ParameterNamesModule());
mapper.setDefaultSetterInfo(JsonSetter.Value.forValueNulls(Nulls.AS_EMPTY));
Product product = mapper.readValue(json, Product.class);
System.out.println(product);
The ParameterNamesModule model is required to allow Jackson to reflect the Product constructor parameters by name, so that #JsonProperty("prices") isn't required anymore.
And JsonSetter.Value.forValueNulls(Nulls.AS_EMPTY) is used to convert missing or null json fields to a list.
Your Product class need to implement Serializable class. It can make consistency data.
class Product implements Serializable {
..............
}
I am trying to deserialize JSON into a custom POJO that I am not able to modify. That POJO has annotations from a different custom internal serialization framework that I'm not able to use. How can I create a custom deserializer that will respect these annotations?
Here is an example POJO:
public class ExampleClass {
#Property(name = "id")
public String id;
#Property(name = "time_windows")
#NotNull
public List<TimeWindow> timeWindows = new ArrayList<>();
public static class TimeWindow {
#Property(name = "start")
public Long start;
#Property(name = "end")
public Long end;
}
}
So in this case, the deserializer would look for fields in the JSON that correspond to the Property annotations, and use the value in that annotation to decide what field to grab. If a property doesn't have the Property annotation, it should be ignored.
I have been going through the Jackson docs but haven't been able to find exactly what I need. Is this a place where an AnnotationIntrospector would be useful? Or possibly a ContextualDeserializer?
Any pointers in the right direction would be greatly appreciated!
Update: I tried implementing the advice in the comments, but without success.
Here is my initial implementation of the introspector:
class CustomAnnotationInspector : JacksonAnnotationIntrospector () {
override fun hasIgnoreMarker(m: AnnotatedMember?): Boolean {
val property = m?.getAnnotation(Property::class.java)
return property == null
}
override fun findNameForDeserialization(a: Annotated?): PropertyName {
val property = a?.getAnnotation(Property::class.java)
return if (property == null) {
super.findNameForDeserialization(a)
} else {
PropertyName(property.name)
}
}
}
And here is where I actually use it:
// Create an empty instance of the request object.
val paramInstance = nonPathParams?.type?.getDeclaredConstructor()?.newInstance()
// Create new object mapper that will write values from
// JSON into the empty object.
val mapper = ObjectMapper()
// Tells the mapper to respect custom annotations.
mapper.setAnnotationIntrospector(CustomAnnotationInspector())
// Write the contents of the request body into the StringWriter
// (this is required for the mapper.writeValue method
val sw = StringWriter()
sw.write(context.bodyAsString)
// Deserialize the contents of the StringWriter
// into the empty POJO.
mapper.writeValue(sw, paramInstance)
Unfortunately it seems that findNameForDeserialization is never called, and none of the JSON values are written into paramInstance. Can anybody spot where I'm going wrong?
Thank you!
Update 2: I changed the code slightly, I'm now able to identify the property names but Jackson is failing to create an instance of the object.
Here's my new code:
val mapper = ObjectMapper()
// Tells the mapper to respect CoreNg annotations.
val introspector = CustomAnnotationInspector()
mapper.setAnnotationIntrospector(introspector)
val paramInstance = mapper.readValue(context.bodyAsString,nonPathParams?.type)
My breakpoints in the custom annotation introspector are getting hit. But I'm getting the following exception:
com.fasterxml.jackson.databind.exc.InvalidDefinitionException: Cannot construct instance of `app.employee.api.employee.BOUpsertEmployeeRequest` (no Creators, like default constructor, exist): cannot deserialize from Object value (no delegate- or property-based Creator)
Here is the POJO I'm trying to deserialize:
public class BOUpsertEmployeeRequest {
public BOUpsertEmployeeRequest () { }
#NotNull
#Property(name = "xref_code")
public String xrefCode;
#Property(name = "first_name")
public String firstName;
#Property(name = "last_name")
public String lastName;
#Property(name = "email_address")
public String emailAddress;
#Property(name = "phone")
public String phone;
#Property(name = "address")
public List<String> address;
#Property(name = "employment_status")
public String employmentStatus;
#Property(name = "pay_type")
public String payType;
#Property(name = "position")
public String position;
#Property(name = "skills")
public List<String> skills;
#Property(name = "gender")
public String gender;
}
As far as I can tell it has a default constructor. Anybody have any idea what the problem is?
Thank you!
Method hasIgnoreMarker is called not only for fields, but also for the constructor, including the virtual one:
Method called to check whether given property is marked to be ignored. This is used to determine whether to ignore properties, on per-property basis, usually combining annotations from multiple accessors (getters, setters, fields, constructor parameters).
In this case you should ignore only fields, that are not marked properly:
static class CustomAnnotationIntrospector extends JacksonAnnotationIntrospector {
#Override
public PropertyName findNameForDeserialization(Annotated a) {
Property property = a.getAnnotation(Property.class);
if (property == null) {
return PropertyName.USE_DEFAULT;
} else {
return PropertyName.construct(property.name());
}
}
#Override
public boolean hasIgnoreMarker(AnnotatedMember m) {
return m instanceof AnnotatedField
&& m.getAnnotation(Property.class) == null;
}
}
Example:
class Pojo {
// #Property(name = "id")
Integer id;
// #Property(name = "number")
Integer number;
#Property(name = "assure")
Boolean assure;
#Property(name = "person")
Map<String, String> person;
}
String json =
"{\"id\" : 1, \"number\" : 12345, \"assure\" : true," +
" \"person\" : {\"name\" : \"John\", \"age\" : 23}}";
ObjectMapper mapper = new ObjectMapper();
mapper.setAnnotationIntrospector(new CustomAnnotationIntrospector());
Pojo pojo = mapper.readValue(json, Pojo.class);
System.out.println(pojo);
Pojo{id=null, number=null, assure=true, person={name=John, age=23}}
Note: Custom Property annotation should have RetentionPolicy.RUNTIME (same as JsonProperty annotation):
#Retention(RetentionPolicy.RUNTIME)
public #interface Property {
String name();
}
I will suggest a different approach:
In the runtime, with the bytecode instrumentation library Byte Buddy and its Java agent, re-annotate the fields with the proper Jackson Annotations. Simply implement the logic via reflection. See the following example:
import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import java.lang.reflect.Field;
import net.bytebuddy.ByteBuddy;
import net.bytebuddy.agent.ByteBuddyAgent;
import net.bytebuddy.description.annotation.AnnotationDescription;
import net.bytebuddy.dynamic.DynamicType.Builder;
import net.bytebuddy.dynamic.DynamicType.Builder.FieldDefinition.Valuable;
import net.bytebuddy.dynamic.loading.ClassReloadingStrategy;
import net.bytebuddy.matcher.ElementMatchers;
#Target(ElementType.FIELD)
#Retention(RetentionPolicy.RUNTIME)
#interface MyJsonIgnore {
}
#Target(ElementType.FIELD)
#Retention(RetentionPolicy.RUNTIME)
#interface MyJsonProperty {
String name();
}
public class Sample {
public static void main(String[] args) throws JsonProcessingException {
ByteBuddyAgent.install();
ClassReloadingStrategy classReloadingStrategy = ClassReloadingStrategy.fromInstalledAgent();
ByteBuddy byteBuddy = new ByteBuddy();
AnnotationDescription jsonIgnoreDesc =
AnnotationDescription.Builder.ofType(JsonIgnore.class).build();
Builder<Person> personBuilder = byteBuddy.redefine(Person.class);
for (Field declaredField : Person.class.getDeclaredFields()) {
Valuable<Person> field = personBuilder.field(ElementMatchers.named(declaredField.getName()));
MyJsonProperty myJsonProperty = declaredField.getAnnotation(MyJsonProperty.class);
if (myJsonProperty != null) {
AnnotationDescription jsonPropertyDesc =
AnnotationDescription.Builder.ofType(JsonProperty.class)
.define("value", myJsonProperty.name())
.build();
personBuilder = field.annotateField(jsonPropertyDesc);
}
MyJsonIgnore myJsonIgnore = declaredField.getAnnotation(MyJsonIgnore.class);
if (myJsonIgnore != null) {
personBuilder = field.annotateField(jsonIgnoreDesc);
}
}
personBuilder.make().load(Sample.class.getClassLoader(), classReloadingStrategy);
Person person = new Person("Utku", "Ozdemir", "Berlin");
ObjectMapper objectMapper = new ObjectMapper();
String jsonString = objectMapper.writeValueAsString(person);
System.out.println(jsonString);
}
}
class Person {
#MyJsonProperty(name = "FIRST")
private String firstName;
#MyJsonProperty(name = "LAST")
private String lastName;
#MyJsonIgnore private String city;
public Person(String firstName, String lastName, String city) {
this.firstName = firstName;
this.lastName = lastName;
this.city = city;
}
}
In the example above, I
create MyJsonProperty and MyJsonIgnore annotations and a Person class for the demonstration purpose
instrument the current Java process with the Byte buddy agent
create a bytebuddy builder to redefine the Person class
loop over the fields of the Person class and check for these annotations
add an additional annotation to each of those fields (on the builder), Jackson's JsonProperty (with the correct field name mapping) and JsonIgnore.
after being done with the fields, make the new class bytecode and load it to the current classloader using the byte buddy agent's class reloading mechanism
write a person object to the stdout.
It prints, as expected:
{"FIRST":"Utku","LAST":"Ozdemir"}
(the field city is ignored)
This solution might feel like an overkill, but on the other side, it is pretty generic solution - with a few changes in the logic, you could handle all the 3rd party classes (which you are not able to modify) instead of handling them case by case.
I am using #JsonIgnore property to ignore some attributes in pojo, but these fields are not ignore in json response after parsing json using Gson library please help.
import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
#JsonIgnoreProperties(ignoreUnknown=true)
public class RideInvite extends RideInviteOld implements Serializable
{
private static final long serialVersionUID = -3729010679180341959L;
private double newFare = -1;
#JsonIgnore
private long prefPickupDropId;
#JsonIgnore
private String pickupLandmark;
#JsonIgnore
private String dropLandmark;
}
using following code to parse
GsonBuilder builder = new GsonBuilder();
Gson gson = builder.create();
jsonText = gson.toJson(msgObject);
Response after parsing
{"newFare":-1.0,"prefPickupDropId":2,"savePickupDropPoints":false,"pickupDropBasedOnTraffic":true,"allowFareChange":true}
here prefPickupDropId and savePickupDropPoints are json ignored but still value attribute is present in json text
I can not use #Expose for fields because my project is build in such away that ignoring fields which are not required json ignore and same pojos are using for preparing http response. This was working fine earlier but recently I am facing this issue
thanks in advance
Approach1:
Instead of using com.fasterxml.jackson.annotation.JsonIgnore you should use
com.google.gson.annotations.Expose to achieve your requirement.
Here is working code snippet -
import java.io.Serializable;
import com.google.gson.annotations.Expose;
public class RideRecord implements Serializable {
#Expose
private double newFare = -1;
private long prefPickupDropId;
private String pickupLandmark;
private String dropLandmark;
public RideRecord(double newFare, long prefPickupDropId, String pickupLandmark, String dropLandmark) {
super();
this.newFare = newFare;
this.prefPickupDropId = prefPickupDropId;
this.pickupLandmark = pickupLandmark;
this.dropLandmark = dropLandmark;
}
}
use the following code to parse:
RideRecord msgObject = new RideRecord(-1.0, 2, "sfo", "powell bart station");
GsonBuilder builder = new GsonBuilder();
builder.excludeFieldsWithoutExposeAnnotation();
Gson gson = builder.create();
String jsonText = gson.toJson(msgObject);
System.out.println(jsonText);
It will give output:
{"newFare":-1.0}
because only newFare is the field which is exposed.
you can play with the #Expose attribute to meet your requirements.
Approach2:
If you don't want to use #Expose then also you can achieve your
requirement by just creating Gson object as below -
RideRecord msgObject = new RideRecord(-1.0, 2, "sfo", "powell bart station");
GsonBuilder builder = new GsonBuilder();
builder.addSerializationExclusionStrategy(new ExclusionStrategy() {
#Override
public boolean shouldSkipField(FieldAttributes fieldAttributes) {
return fieldAttributes.getName().equals("prefPickupDropId") || fieldAttributes.getName().equals("pickupLandmark") || fieldAttributes.getName().equals("dropLandmark");
}
#Override
public boolean shouldSkipClass(Class<?> arg0) {
// TODO Auto-generated method stub
return false;
}
});
Gson gson = builder.create();
String jsonText = gson.toJson(msgObject);
System.out.println(jsonText);
In this case also you will get the same output :
{"newFare":-1.0}
You're using Jackson with GSON and those are too separate frameworks.
One of the approaches is to initialize Gson to exclude fields that do not have the expose annotation.
Gson gson = new GsonBuilder().excludeFieldsWithoutExposeAnnotation().create();
Then mark every field you need to use with the #Expose annotation:
ex:
class MyClass {
#Expose public String name;
}
I want to be able to access properties from a JSON string within my Java action method. The string is available by simply saying myJsonString = object.getJson(). Below is an example of what the string can look like:
{
'title': 'ComputingandInformationsystems',
'id': 1,
'children': 'true',
'groups': [{
'title': 'LeveloneCIS',
'id': 2,
'children': 'true',
'groups': [{
'title': 'IntroToComputingandInternet',
'id': 3,
'children': 'false',
'groups': []
}]
}]
}
In this string every JSON object contains an array of other JSON objects. The intention is to extract a list of IDs where any given object possessing a group property that contains other JSON objects. I looked at Google's Gson as a potential JSON plugin. Can anyone offer some form of guidance as to how I can generate Java from this JSON string?
I looked at Google's Gson as a potential JSON plugin. Can anyone offer some form of guidance as to how I can generate Java from this JSON string?
Google Gson supports generics and nested beans. The [] in JSON represents an array and should map to a Java collection such as List or just a plain Java array. The {} in JSON represents an object and should map to a Java Map or just some JavaBean class.
You have a JSON object with several properties of which the groups property represents an array of nested objects of the very same type. This can be parsed with Gson the following way:
package com.stackoverflow.q1688099;
import java.util.List;
import com.google.gson.Gson;
public class Test {
public static void main(String... args) throws Exception {
String json =
"{"
+ "'title': 'Computing and Information systems',"
+ "'id' : 1,"
+ "'children' : 'true',"
+ "'groups' : [{"
+ "'title' : 'Level one CIS',"
+ "'id' : 2,"
+ "'children' : 'true',"
+ "'groups' : [{"
+ "'title' : 'Intro To Computing and Internet',"
+ "'id' : 3,"
+ "'children': 'false',"
+ "'groups':[]"
+ "}]"
+ "}]"
+ "}";
// Now do the magic.
Data data = new Gson().fromJson(json, Data.class);
// Show it.
System.out.println(data);
}
}
class Data {
private String title;
private Long id;
private Boolean children;
private List<Data> groups;
public String getTitle() { return title; }
public Long getId() { return id; }
public Boolean getChildren() { return children; }
public List<Data> getGroups() { return groups; }
public void setTitle(String title) { this.title = title; }
public void setId(Long id) { this.id = id; }
public void setChildren(Boolean children) { this.children = children; }
public void setGroups(List<Data> groups) { this.groups = groups; }
public String toString() {
return String.format("title:%s,id:%d,children:%s,groups:%s", title, id, children, groups);
}
}
Fairly simple, isn't it? Just have a suitable JavaBean and call Gson#fromJson().
See also:
Json.org - Introduction to JSON
Gson User Guide - Introduction to Gson
Bewaaaaare of Gson! It's very cool, very great, but the second you want to do anything other than simple objects, you could easily need to start building your own serializers (which isn't that hard).
Also, if you have an array of Objects, and you deserialize some json into that array of Objects, the true types are LOST! The full objects won't even be copied! Use XStream.. Which, if using the jsondriver and setting the proper settings, will encode ugly types into the actual json, so that you don't loose anything. A small price to pay (ugly json) for true serialization.
Note that Jackson fixes these issues, and is faster than GSON.
Oddly, the only decent JSON processor mentioned so far has been GSON.
Here are more good choices:
Jackson (Github) -- powerful data binding (JSON to/from POJOs), streaming (ultra fast), tree model (convenient for untyped access)
Flex-JSON -- highly configurable serialization
EDIT (Aug/2013):
One more to consider:
Genson -- functionality similar to Jackson, aimed to be easier to configure by developer
Or with Jackson:
String json = "...";
ObjectMapper m = new ObjectMapper();
Set<Product> products = m.readValue(json, new TypeReference<Set<Product>>() {});
Easy and working java code to convert JSONObject to Java Object
Employee.java
import java.util.HashMap;
import java.util.Map;
import javax.annotation.Generated;
import com.fasterxml.jackson.annotation.JsonAnyGetter;
import com.fasterxml.jackson.annotation.JsonAnySetter;
import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.annotation.JsonPropertyOrder;
#JsonInclude(JsonInclude.Include.NON_NULL)
#Generated("org.jsonschema2pojo")
#JsonPropertyOrder({
"id",
"firstName",
"lastName"
})
public class Employee {
#JsonProperty("id")
private Integer id;
#JsonProperty("firstName")
private String firstName;
#JsonProperty("lastName")
private String lastName;
#JsonIgnore
private Map<String, Object> additionalProperties = new HashMap<String, Object>();
/**
*
* #return
* The id
*/
#JsonProperty("id")
public Integer getId() {
return id;
}
/**
*
* #param id
* The id
*/
#JsonProperty("id")
public void setId(Integer id) {
this.id = id;
}
/**
*
* #return
* The firstName
*/
#JsonProperty("firstName")
public String getFirstName() {
return firstName;
}
/**
*
* #param firstName
* The firstName
*/
#JsonProperty("firstName")
public void setFirstName(String firstName) {
this.firstName = firstName;
}
/**
*
* #return
* The lastName
*/
#JsonProperty("lastName")
public String getLastName() {
return lastName;
}
/**
*
* #param lastName
* The lastName
*/
#JsonProperty("lastName")
public void setLastName(String lastName) {
this.lastName = lastName;
}
#JsonAnyGetter
public Map<String, Object> getAdditionalProperties() {
return this.additionalProperties;
}
#JsonAnySetter
public void setAdditionalProperty(String name, Object value) {
this.additionalProperties.put(name, value);
}
}
LoadFromJSON.java
import org.codehaus.jettison.json.JSONObject;
import com.fasterxml.jackson.databind.ObjectMapper;
public class LoadFromJSON {
public static void main(String args[]) throws Exception {
JSONObject json = new JSONObject();
json.put("id", 2);
json.put("firstName", "hello");
json.put("lastName", "world");
byte[] jsonData = json.toString().getBytes();
ObjectMapper mapper = new ObjectMapper();
Employee employee = mapper.readValue(jsonData, Employee.class);
System.out.print(employee.getLastName());
}
}
If, by any change, you are in an application which already uses http://restfb.com/ then you can do:
import com.restfb.json.JsonObject;
...
JsonObject json = new JsonObject(jsonString);
json.get("title");
etc.
HashMap keyArrayList = new HashMap();
Iterator itr = yourJson.keys();
while (itr.hasNext())
{
String key = (String) itr.next();
keyArrayList.put(key, yourJson.get(key).toString());
}
Depending on the input JSON format(string/file) create a jSONString. Sample Message class object corresponding to JSON can be obtained as below:
Message msgFromJSON = new ObjectMapper().readValue(jSONString, Message.class);
If you use any kind of special maps with keys or values also of special maps, you will find it's not contemplated by the implementation of google.
What's wrong with the standard stuff?
JSONObject jsonObject = new JSONObject(someJsonString);
JSONArray jsonArray = jsonObject.getJSONArray("someJsonArray");
String value = jsonArray.optJSONObject(i).getString("someJsonValue");
Pass your JSON file in this way, it will return the object.
File file = new File("D:\\Coding\\tickets\\temp.json");
ObjectMapper om = new ObjectMapper();
Profile profile = om.readValue(file, new TypeReference<Profile>() {});
Give boon a try:
https://github.com/RichardHightower/boon
It is wicked fast:
https://github.com/RichardHightower/json-parsers-benchmark
Don't take my word for it... check out the gatling benchmark.
https://github.com/gatling/json-parsers-benchmark
(Up to 4x is some cases, and out of the 100s of test. It also has a index overlay mode that is even faster. It is young but already has some users.)
It can parse JSON to Maps and Lists faster than any other lib can parse to a JSON DOM and that is without Index Overlay mode. With Boon Index Overlay mode, it is even faster.
It also has a very fast JSON lax mode and a PLIST parser mode. :) (and has a super low memory, direct from bytes mode with UTF-8 encoding on the fly).
It also has the fastest JSON to JavaBean mode too.
It is new, but if speed and simple API is what you are looking for, I don't think there is a faster or more minimalist API.
The easiest way is that you can use this softconvertvalue method which is a custom method in which you can convert jsonData into your specific Dto class.
Dto response = softConvertValue(jsonData, Dto.class);
public static <T> T softConvertValue(Object fromValue, Class<T> toValueType)
{
ObjectMapper objMapper = new ObjectMapper();
return objMapper
.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false)
.convertValue(fromValue, toValueType);
}
<!-- GSON -->
<dependency>
<groupId>com.google.code.gson</groupId>
<artifactId>gson</artifactId>
<version>2.8.7</version>
</dependency>
#Test
void readListJsonFromFileTest() throws IOException {
Type type = new TypeToken<List<SimplePojo>>(){}.getType();
String fromJsonFile = readFromJsonFile("json/simplePojoJsonList.json");
List<SimplePojo> pojoList = gson.fromJson(fromJsonFile, type);
Assertions.assertNotNull(pojoList);
}
#Test
void readJsonFromFileTest() throws IOException {
Type type = new TypeToken<SimplePojo>(){}.getType();
String fromJsonFile = readFromJsonFile("json/simplePojoJson.json");
SimplePojo simplePojo = gson.fromJson(fromJsonFile, type);
Assertions.assertNotNull(simplePojo);
}
String readFromJsonFile(String pathToJson) throws IOException {
InputStream resource = new ClassPathResource(pathToJson).getInputStream();
String json = StreamUtils.copyToString(resource, StandardCharsets.UTF_8);
return json;
}
I want to be able to access properties from a JSON string within my Java action method. The string is available by simply saying myJsonString = object.getJson(). Below is an example of what the string can look like:
{
'title': 'ComputingandInformationsystems',
'id': 1,
'children': 'true',
'groups': [{
'title': 'LeveloneCIS',
'id': 2,
'children': 'true',
'groups': [{
'title': 'IntroToComputingandInternet',
'id': 3,
'children': 'false',
'groups': []
}]
}]
}
In this string every JSON object contains an array of other JSON objects. The intention is to extract a list of IDs where any given object possessing a group property that contains other JSON objects. I looked at Google's Gson as a potential JSON plugin. Can anyone offer some form of guidance as to how I can generate Java from this JSON string?
I looked at Google's Gson as a potential JSON plugin. Can anyone offer some form of guidance as to how I can generate Java from this JSON string?
Google Gson supports generics and nested beans. The [] in JSON represents an array and should map to a Java collection such as List or just a plain Java array. The {} in JSON represents an object and should map to a Java Map or just some JavaBean class.
You have a JSON object with several properties of which the groups property represents an array of nested objects of the very same type. This can be parsed with Gson the following way:
package com.stackoverflow.q1688099;
import java.util.List;
import com.google.gson.Gson;
public class Test {
public static void main(String... args) throws Exception {
String json =
"{"
+ "'title': 'Computing and Information systems',"
+ "'id' : 1,"
+ "'children' : 'true',"
+ "'groups' : [{"
+ "'title' : 'Level one CIS',"
+ "'id' : 2,"
+ "'children' : 'true',"
+ "'groups' : [{"
+ "'title' : 'Intro To Computing and Internet',"
+ "'id' : 3,"
+ "'children': 'false',"
+ "'groups':[]"
+ "}]"
+ "}]"
+ "}";
// Now do the magic.
Data data = new Gson().fromJson(json, Data.class);
// Show it.
System.out.println(data);
}
}
class Data {
private String title;
private Long id;
private Boolean children;
private List<Data> groups;
public String getTitle() { return title; }
public Long getId() { return id; }
public Boolean getChildren() { return children; }
public List<Data> getGroups() { return groups; }
public void setTitle(String title) { this.title = title; }
public void setId(Long id) { this.id = id; }
public void setChildren(Boolean children) { this.children = children; }
public void setGroups(List<Data> groups) { this.groups = groups; }
public String toString() {
return String.format("title:%s,id:%d,children:%s,groups:%s", title, id, children, groups);
}
}
Fairly simple, isn't it? Just have a suitable JavaBean and call Gson#fromJson().
See also:
Json.org - Introduction to JSON
Gson User Guide - Introduction to Gson
Bewaaaaare of Gson! It's very cool, very great, but the second you want to do anything other than simple objects, you could easily need to start building your own serializers (which isn't that hard).
Also, if you have an array of Objects, and you deserialize some json into that array of Objects, the true types are LOST! The full objects won't even be copied! Use XStream.. Which, if using the jsondriver and setting the proper settings, will encode ugly types into the actual json, so that you don't loose anything. A small price to pay (ugly json) for true serialization.
Note that Jackson fixes these issues, and is faster than GSON.
Oddly, the only decent JSON processor mentioned so far has been GSON.
Here are more good choices:
Jackson (Github) -- powerful data binding (JSON to/from POJOs), streaming (ultra fast), tree model (convenient for untyped access)
Flex-JSON -- highly configurable serialization
EDIT (Aug/2013):
One more to consider:
Genson -- functionality similar to Jackson, aimed to be easier to configure by developer
Or with Jackson:
String json = "...";
ObjectMapper m = new ObjectMapper();
Set<Product> products = m.readValue(json, new TypeReference<Set<Product>>() {});
Easy and working java code to convert JSONObject to Java Object
Employee.java
import java.util.HashMap;
import java.util.Map;
import javax.annotation.Generated;
import com.fasterxml.jackson.annotation.JsonAnyGetter;
import com.fasterxml.jackson.annotation.JsonAnySetter;
import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.annotation.JsonPropertyOrder;
#JsonInclude(JsonInclude.Include.NON_NULL)
#Generated("org.jsonschema2pojo")
#JsonPropertyOrder({
"id",
"firstName",
"lastName"
})
public class Employee {
#JsonProperty("id")
private Integer id;
#JsonProperty("firstName")
private String firstName;
#JsonProperty("lastName")
private String lastName;
#JsonIgnore
private Map<String, Object> additionalProperties = new HashMap<String, Object>();
/**
*
* #return
* The id
*/
#JsonProperty("id")
public Integer getId() {
return id;
}
/**
*
* #param id
* The id
*/
#JsonProperty("id")
public void setId(Integer id) {
this.id = id;
}
/**
*
* #return
* The firstName
*/
#JsonProperty("firstName")
public String getFirstName() {
return firstName;
}
/**
*
* #param firstName
* The firstName
*/
#JsonProperty("firstName")
public void setFirstName(String firstName) {
this.firstName = firstName;
}
/**
*
* #return
* The lastName
*/
#JsonProperty("lastName")
public String getLastName() {
return lastName;
}
/**
*
* #param lastName
* The lastName
*/
#JsonProperty("lastName")
public void setLastName(String lastName) {
this.lastName = lastName;
}
#JsonAnyGetter
public Map<String, Object> getAdditionalProperties() {
return this.additionalProperties;
}
#JsonAnySetter
public void setAdditionalProperty(String name, Object value) {
this.additionalProperties.put(name, value);
}
}
LoadFromJSON.java
import org.codehaus.jettison.json.JSONObject;
import com.fasterxml.jackson.databind.ObjectMapper;
public class LoadFromJSON {
public static void main(String args[]) throws Exception {
JSONObject json = new JSONObject();
json.put("id", 2);
json.put("firstName", "hello");
json.put("lastName", "world");
byte[] jsonData = json.toString().getBytes();
ObjectMapper mapper = new ObjectMapper();
Employee employee = mapper.readValue(jsonData, Employee.class);
System.out.print(employee.getLastName());
}
}
If, by any change, you are in an application which already uses http://restfb.com/ then you can do:
import com.restfb.json.JsonObject;
...
JsonObject json = new JsonObject(jsonString);
json.get("title");
etc.
HashMap keyArrayList = new HashMap();
Iterator itr = yourJson.keys();
while (itr.hasNext())
{
String key = (String) itr.next();
keyArrayList.put(key, yourJson.get(key).toString());
}
Depending on the input JSON format(string/file) create a jSONString. Sample Message class object corresponding to JSON can be obtained as below:
Message msgFromJSON = new ObjectMapper().readValue(jSONString, Message.class);
If you use any kind of special maps with keys or values also of special maps, you will find it's not contemplated by the implementation of google.
What's wrong with the standard stuff?
JSONObject jsonObject = new JSONObject(someJsonString);
JSONArray jsonArray = jsonObject.getJSONArray("someJsonArray");
String value = jsonArray.optJSONObject(i).getString("someJsonValue");
Pass your JSON file in this way, it will return the object.
File file = new File("D:\\Coding\\tickets\\temp.json");
ObjectMapper om = new ObjectMapper();
Profile profile = om.readValue(file, new TypeReference<Profile>() {});
Give boon a try:
https://github.com/RichardHightower/boon
It is wicked fast:
https://github.com/RichardHightower/json-parsers-benchmark
Don't take my word for it... check out the gatling benchmark.
https://github.com/gatling/json-parsers-benchmark
(Up to 4x is some cases, and out of the 100s of test. It also has a index overlay mode that is even faster. It is young but already has some users.)
It can parse JSON to Maps and Lists faster than any other lib can parse to a JSON DOM and that is without Index Overlay mode. With Boon Index Overlay mode, it is even faster.
It also has a very fast JSON lax mode and a PLIST parser mode. :) (and has a super low memory, direct from bytes mode with UTF-8 encoding on the fly).
It also has the fastest JSON to JavaBean mode too.
It is new, but if speed and simple API is what you are looking for, I don't think there is a faster or more minimalist API.
The easiest way is that you can use this softconvertvalue method which is a custom method in which you can convert jsonData into your specific Dto class.
Dto response = softConvertValue(jsonData, Dto.class);
public static <T> T softConvertValue(Object fromValue, Class<T> toValueType)
{
ObjectMapper objMapper = new ObjectMapper();
return objMapper
.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false)
.convertValue(fromValue, toValueType);
}
<!-- GSON -->
<dependency>
<groupId>com.google.code.gson</groupId>
<artifactId>gson</artifactId>
<version>2.8.7</version>
</dependency>
#Test
void readListJsonFromFileTest() throws IOException {
Type type = new TypeToken<List<SimplePojo>>(){}.getType();
String fromJsonFile = readFromJsonFile("json/simplePojoJsonList.json");
List<SimplePojo> pojoList = gson.fromJson(fromJsonFile, type);
Assertions.assertNotNull(pojoList);
}
#Test
void readJsonFromFileTest() throws IOException {
Type type = new TypeToken<SimplePojo>(){}.getType();
String fromJsonFile = readFromJsonFile("json/simplePojoJson.json");
SimplePojo simplePojo = gson.fromJson(fromJsonFile, type);
Assertions.assertNotNull(simplePojo);
}
String readFromJsonFile(String pathToJson) throws IOException {
InputStream resource = new ClassPathResource(pathToJson).getInputStream();
String json = StreamUtils.copyToString(resource, StandardCharsets.UTF_8);
return json;
}