Strict JSON parsing with Google's Gson? - java

Suppose I am using Google's Gson library to parse JSON into Java data structures.
Is there an easy way to throw an exception if there is a Java field that has no corresponding JSON? That is, I wish to require the JSON to have all the fields in the Java structure.

Gson doesn't have a JSON schema validation feature to specify that a particular element must be present, and it doesn't have a way to specify that a Java member must be populated. It might be nice to have such a feature available, such as with an #Required annotation. Head on over to the Gson Issues List and put in an enhancement request.
With Gson, you could enforce that specified JSON elements are present with a custom deserializer.
// output:
// [MyObject: element1=value1, element2=value2, element3=value3]
// [MyObject: element1=value1, element2=value2, element3=null]
// Exception in thread "main" com.google.gson.JsonParseException: Required Field Not Found: element2
import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.List;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.google.gson.JsonDeserializationContext;
import com.google.gson.JsonDeserializer;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import com.google.gson.JsonParseException;
public class Foo
{
static String jsonInput1 = "{\"element1\":\"value1\",\"element2\":\"value2\",\"element3\":\"value3\"}";
static String jsonInput2 = "{\"element1\":\"value1\",\"element2\":\"value2\"}";
static String jsonInput3 = "{\"element1\":\"value1\",\"element3\":\"value3\"}";
public static void main(String[] args)
{
GsonBuilder gsonBuilder = new GsonBuilder();
MyDeserializer deserializer = new MyDeserializer();
deserializer.registerRequiredField("element2");
gsonBuilder.registerTypeAdapter(MyObject.class, deserializer);
Gson gson = gsonBuilder.create();
MyObject object1 = gson.fromJson(jsonInput1, MyObject.class);
System.out.println(object1);
MyObject object2 = gson.fromJson(jsonInput2, MyObject.class);
System.out.println(object2);
MyObject object3 = gson.fromJson(jsonInput3, MyObject.class);
System.out.println(object3);
}
}
class MyObject
{
String element1;
String element2;
String element3;
#Override
public String toString()
{
return String.format(
"[MyObject: element1=%s, element2=%s, element3=%s]",
element1, element2, element3);
}
}
class MyDeserializer implements JsonDeserializer<MyObject>
{
List<String> requiredFields = new ArrayList<String>();
void registerRequiredField(String fieldName)
{
requiredFields.add(fieldName);
}
#Override
public MyObject deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context)
throws JsonParseException
{
JsonObject jsonObject = (JsonObject) json;
for (String fieldName : requiredFields)
{
if (jsonObject.get(fieldName) == null)
{
throw new JsonParseException("Required Field Not Found: " + fieldName);
}
}
return new Gson().fromJson(json, MyObject.class);
}
}
A preferable approach might be to use an API that provides JSON Schema validation. Jackson has at least a rudimentary implementation available. JSON Tools looks to have a more mature one.
Here's an example with Jackson.
// output:
// Validating jsonInput1...
// Validating jsonInput2...
// Validating jsonInput3...
// $.element2: is missing and it is not optional
// [MyObject: element1=value1, element2=value2, element3=value3]
// [MyObject: element1=value1, element2=value2, element3=null]
// [MyObject: element1=value1, element2=null, element3=value3]
import java.util.List;
import org.codehaus.jackson.map.ObjectMapper;
import eu.vahlas.json.schema.JSONSchema;
import eu.vahlas.json.schema.JSONSchemaProvider;
import eu.vahlas.json.schema.impl.JacksonSchemaProvider;
public class Foo
{
static String jsonSchema =
"{" +
"\"description\":\"Serialized MyObject Specification\"," +
"\"type\":[\"object\"]," +
"\"properties\":" +
"{" +
"\"element1\":{\"type\":\"string\"}," +
"\"element2\":{\"type\":\"string\",\"optional\":false}," +
"\"element3\":{\"type\":\"string\",\"optional\":true}" +
"}" +
"}";;
static String jsonInput1 = "{\"element1\":\"value1\",\"element2\":\"value2\",\"element3\":\"value3\"}";
static String jsonInput2 = "{\"element1\":\"value1\",\"element2\":\"value2\"}";
static String jsonInput3 = "{\"element1\":\"value1\",\"element3\":\"value3\"}";
public static void main(String[] args) throws Exception
{
ObjectMapper mapper = new ObjectMapper();
JSONSchemaProvider schemaProvider = new JacksonSchemaProvider(mapper);
JSONSchema schema = schemaProvider.getSchema(jsonSchema);
System.out.println("Validating jsonInput1...");
validateAndLogErrors(jsonInput1, schema);
System.out.println("Validating jsonInput2...");
validateAndLogErrors(jsonInput2, schema);
System.out.println("Validating jsonInput3...");
validateAndLogErrors(jsonInput3, schema);
MyObject object1 = mapper.readValue(jsonInput1, MyObject.class);
System.out.println(object1);
MyObject object2 = mapper.readValue(jsonInput2, MyObject.class);
System.out.println(object2);
MyObject object3 = mapper.readValue(jsonInput3, MyObject.class);
System.out.println(object3);
}
static void validateAndLogErrors(String jsonInput, JSONSchema schema)
{
List<String> errors = schema.validate(jsonInput);
for (String error : errors)
{
System.out.println(error);
}
}
}
class MyObject
{
String element1;
String element2;
String element3;
void setElement1(String element1)
{
this.element1 = element1;
}
void setElement2(String element2)
{
this.element2 = element2;
}
void setElement3(String element3)
{
this.element3 = element3;
}
#Override
public String toString()
{
return String.format(
"[MyObject: element1=%s, element2=%s, element3=%s]",
element1, element2, element3);
}
}

You can recursively verify whether the json contains fields that are not declared in the class :
private static List<String> verifyElement(JsonObject element, Class klass) throws NoSuchFieldException, IllegalAccessException {
List<String> unknownFields = new ArrayList<>();
Set<String> classFields = new HashSet<>();
for (Field field : klass.getDeclaredFields()) {
if (!Modifier.isPublic(field.getModifiers())) {
throw new IllegalArgumentException("All fields must be public. Please correct this field :" + field);
}
}
for (Field field : klass.getFields()) {
classFields.add(field.getName());
}
// Verify recursively that the class contains every
for (Map.Entry<String, JsonElement> entry : element.entrySet()) {
if (!classFields.contains(entry.getKey())) {
unknownFields.add(klass.getCanonicalName() + "::" + entry.getKey() + "\n");
} else {
Field field = klass.getField(entry.getKey());
Class fieldClass = field.getType();
if (!fieldClass.isPrimitive() && entry.getValue().isJsonObject()) {
List<String> elementErrors = verifyElement(entry.getValue().getAsJsonObject(), fieldClass);
unknownFields.addAll(elementErrors);
}
}
}
return unknownFields;
}

Related

Custom deserialization with GSON using a TypeAdapter

The code works and deserializes the JSON string
{"d": [{"d1": "D1"}, {"d2": "D2"}]}
to the dto object, of type Dto, using GSON and a custom TypeAdapter<Dto>. However, the DtoAdapter class seems a bit complicated to me. Is there a better implementation for the DtoAdapter? And is there a better way to custom deserialize the JSON string using GSON to get the same result?
public class Main {
public static void main(String[] args) {
GsonBuilder builder = new GsonBuilder();
builder.registerTypeAdapter(Dto.class, new DtoAdapter());
Gson gson = builder.create();
String json = "{\"d\": [{\"d1\": \"D1\"}, {\"d2\": \"D2\"}]}";
Dto dto = gson.fromJson(json, Dto.class);
System.out.println(dto); // Dto(items=[Dto.Item(name=D1 custom case 1), Dto.Item(name=D2 custom case 2)])
}
}
class DtoAdapter extends TypeAdapter<Dto> {
#Override
public Dto read(JsonReader reader) throws IOException {
Dto dto = new Dto();
reader.beginObject(); // Start JSON string {...}
while (reader.hasNext()) {
switch (reader.nextName()) {
case "d":
reader.beginArray(); // Start Array {"d": [...]}
while (reader.hasNext()) {
reader.beginObject(); // Start Object in Array {"d": [{...}, ]}
Dto.Item item = new Dto.Item();
switch (reader.nextName()) {
case "d1":
item.setName(reader.nextString() + " custom case 1");
dto.getItems().add(item);
break;
case "d2":
item.setName(reader.nextString() + " custom case 2");
dto.getItems().add(item);
break;
}
reader.endObject(); // End Object in Array
}
reader.endArray(); // End Array
break;
}
}
reader.endObject(); // End JSON string
return dto;
}
#Override
public void write(JsonWriter writer, Dto dto) throws IOException {
// Serialize
}
}
#Data
public class Dto {
List<Item> items = new ArrayList<>();
#Data
public static class Item {
private String name;
}
}
You can write custom deserilizer like this:
import com.google.gson.JsonArray;
import com.google.gson.JsonDeserializationContext;
import com.google.gson.JsonDeserializer;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import com.google.gson.JsonParseException;
import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
public class CustomDeserilizer implements JsonDeserializer<Dto> {
#Override
public Dto deserialize(JsonElement jsonElement, Type type, JsonDeserializationContext jsonDeserializationContext) throws JsonParseException {
JsonObject jobject = jsonElement.getAsJsonObject();
JsonArray jsonArray = jobject.getAsJsonArray("d");
Dto dto = new Dto();
List<Dto.Item> items = new ArrayList<>();
for (int i = 0; i < jsonArray.size(); i++) {
Dto.Item item = new Dto.Item();
JsonObject object = jsonArray.get(i).getAsJsonObject();
if(Objects.nonNull(object.get("d1"))){
item.setName(object.get("d1").getAsString()+ " custom case 1");
}
if(Objects.nonNull(object.get("d2"))){
item.setName(object.get("d2").getAsString()+ " custom case 2");
}
items.add(item);
}
dto.setItems(items);
return dto;
}
}
And if you test it using below code:
GsonBuilder gsonBuilder = new GsonBuilder();
String json = "{\"d\": [{\"d1\": \"D1\"}, {\"d2\": \"D2\"}]}";
gsonBuilder.registerTypeAdapter(Dto.class, new CustomDeserilizer());
Gson customGson = gsonBuilder.create();
Dto customObject = customGson.fromJson(json, Dto.class);
System.out.println(customObject);
You should see output like this:
Dto{items=[Item{name='D1 custom case 1'}, Item{name='D2 custom case 2'}]}
Seems you could create a DTO property with an alternate name for your list item.
public class D {
#SerializedName(value = "d1", alternate = "d2")
#Expose
private String d1;
public String getD1() {
return d1;
}
public void setD1(String d1) {
this.d1 = d1;
}
}

How to deserialize a string separated by comma to list with Jackson commonly?

I have a json like:
{
"names": "John, Tom",
"values": "8, 9",
"statuses": "yes, no"
}
and want to deserialize to:
class Bean {
private List<String> names;
private List<Integer> values;
private List<StatusEnum> statuses;
}
I know that implementing StringToStringListDeserializer, StringToIntegerListDeserializer, and StringToStatusEnumListDeserializer separately is practicable. But there are many other content types, including customized types. I tried:
public class StringToListDeserializer<T> extends JsonDeserializer<List<T>> implements ContextualDeserializer
public List<T> deserialize(JsonParser p, DeserializationContext context) throws IOException {
JavaType javaType = property.getType();
if (p.hasToken(JsonToken.VALUE_STRING)) {
String text = p.getText();
if (StringUtils.isBlank(text)) {
return null;
}
List<T> list = new LinkedList<>();
JavaType contentType = javaType.getContentType();
JsonDeserializer<Object> deserializer = context.findNonContextualValueDeserializer(contentType);
for (String s : text.split(DELIMITER)) {
// todo how to deserialize the string to a known type?
}
return list;
}
return context.readValue(p, javaType);
}
and i don't know how to deserialize the string to the known content type. Is there any way to implement a universal deserializer?
To avoid manual deserialisation and handling all possible types we can use a fact that all items on the list are also JSON elements when we wrap them with a quote (") char.
So, we can convert John, Tom to a "John", "Tom", 8, 9 to "8", "9" and so on.
We can use default Jackson behaviour which allows to handle unexpected tokens. In our case whenever: STRING token appears when JSON ARRAY was expected. To handle these cases we can use com.fasterxml.jackson.databind.deser.DeserializationProblemHandler class. It could look like below:
class ComaSeparatedValuesDeserializationProblemHandler extends DeserializationProblemHandler {
#Override
public Object handleUnexpectedToken(DeserializationContext ctxt, JavaType targetType, JsonToken token, JsonParser parser, String failureMsg) throws IOException {
if (token == JsonToken.VALUE_STRING && targetType.isCollectionLikeType()) {
return deserializeAsList(targetType, parser);
}
return super.handleUnexpectedToken(ctxt, targetType, token, parser, failureMsg);
}
private Object deserializeAsList(JavaType listType, JsonParser parser) throws IOException {
String[] values = readValues(parser);
ObjectMapper mapper = (ObjectMapper) parser.getCodec();
JavaType itemType = listType.getContentType();
List<Object> result = new ArrayList<>();
for (String value : values) {
result.add(convertToItemType(mapper, itemType, value));
}
return result;
}
private Object convertToItemType(ObjectMapper mapper, JavaType contentType, String value) throws IOException {
final String json = "\"" + value.trim() + "\"";
return mapper.readValue(json, contentType);
}
private String[] readValues(JsonParser p) throws IOException {
final String text = p.getText();
return text.split(",");
}
}
Example usage:
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.core.JsonToken;
import com.fasterxml.jackson.databind.DeserializationContext;
import com.fasterxml.jackson.databind.JavaType;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.deser.DeserializationProblemHandler;
import com.fasterxml.jackson.databind.json.JsonMapper;
import com.google.common.base.Joiner;
import lombok.Data;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
public class ConvertStringToCollectionApp {
public static void main(String[] args) throws IOException {
File jsonFile = new File("./resource/test.json").getAbsoluteFile();
ObjectMapper mapper = JsonMapper.builder()
.addHandler(new ComaSeparatedValuesDeserializationProblemHandler())
.build();
Bean bean = mapper.readValue(jsonFile, Bean.class);
print(bean.getNames());
print(bean.getValues());
print(bean.getStatuses());
}
private static void print(List<?> values) {
values.stream().findFirst().ifPresent(value -> System.out.print(value.getClass().getSimpleName() + "s: "));
System.out.println(Joiner.on(", ").join(values));
}
}
#Data
class Bean {
private List<String> names;
private List<Integer> values;
private List<StatusEnum> statuses;
}
enum StatusEnum {
yes, no
}
Above app for your JSON payload prints:
Strings: John, Tom
Integers: 8, 9
StatusEnums: yes, no
I used Lombok and Guava libraries just to make it simple and short but they are not mandatory to make it work.
Your Bean doesn't correctly represents the JSON. The correct version should look something like below
class Bean {
private String names;
private Integer values;
private String statuses;
}
And you can use Object Mapper
ObjectMapper objectMapper = new ObjectMapper();
Bean bean = objectMapper.readValue(json, Bean.class);
Finally, you can break down your Bean object to list of names, values and status for your further usages.

Java, Jackson, Custom deserializer with extended classes

Using Jackson 2.10., I am trying to write a custom deserializer for a base class, but I have to deserialize fields with unknown field name. Then there are extended class that can also extend this serializer.
I have tried to use the #AnyGetter, and #AnySetter to accomplish it, and it kind of does work. Now I am just wondering if there is a way to do it through a custom deserializer.
I could do it with a base class, but it fails when some class extends it.
Here is the sample of what I have done.
The following is just the base class and its serializer and how I used in in the main.
//BaseClass
#JsonDeserialize(using = BaseClassDeserializer.class)
public static class BaseClass {
private ObjectNode customFields = JsonNodeFactory.instance.objectNode();
private int baseInt;
public int getBaseInt() {
return baseInt;
}
public void setBaseInt(int baseInt) {
this.baseInt = baseInt;
}
public JsonNode getCustomFields() {
return customFields;
}
public void setCustomFields(ObjectNode customFields) {
this.customFields = customFields;
}
public void putCustomFields(String key, JsonNode node) {
this.customFields.set(key, node);
}
}
// BaseClassDeserializer
public static class BaseClassDeserializer extends StdDeserializer<BaseClass> {
public BaseClassDeserializer() {
this(null);
}
public BaseClassDeserializer(Class<?> vc) {
super(vc);
}
#Override
public BaseClass deserialize(JsonParser jsonParser, DeserializationContext deserializationContext)
throws IOException, JsonProcessingException {
BaseClass result = new BaseClass();
JsonNode node = jsonParser.getCodec().readTree(jsonParser);
result.setBaseInt((Integer) ((IntNode) node.get("baseInt")).numberValue());
node.fieldNames();
Iterator<String> iterator = node.fieldNames();
while (iterator.hasNext()) {
String fieldName = iterator.next();
if (!"baseInt".equals(fieldName)) {
result.putCustomFields(fieldName, node.get(fieldName));
}
}
return result;
}
}
// main
public static void main(String[] args) throws JsonProcessingException {
String json = "{\n"
+ "\t\"baseInt\": 1,\n"
+ "\t\"customObject\" : {\n"
+ "\t\t\"key\": \"value\"\n"
+ "\t},\n"
+ "\t\"customString\" : \"STRING\",\n"
+ "\t\"extendedString\" : \"STRING\"\n"
+ "}";
ObjectMapper mapper = new ObjectMapper();
BaseClass myClass = mapper.readValue(json, BaseClass.class);
}
By going looking through the debugger, the fields are successfully loaded.
Now I am trying to extend BaseClass
// ExtendedClass
public static class ExtendedClass extends BaseClass {
#JsonProperty("extendedString")
private String extendedString;
public String getExtendedString() {
return extendedString;
}
public void setExtendedString(String extendedString) {
this.extendedString = extendedString;
}
}
public static void main(String[] args) throws JsonProcessingException {
String json = "{\n"
+ "\t\"baseInt\": 1,\n"
+ "\t\"customObject\" : {\n"
+ "\t\t\"key\": \"value\"\n"
+ "\t},\n"
+ "\t\"customString\" : \"STRING\",\n"
+ "\t\"extendedString\" : \"STRING\"\n"
+ "}";
ObjectMapper mapper = new ObjectMapper();
ExtendedClass myClass = mapper.readValue(json, ExtendedClass.class);
}
And this crashes with a
BaseClass cannot be cast to ExtendedClass exception.
I am guessing I have to pass along the deserialization to the child class' deserializer, but I cannot figure out how.
In your deserialiser you always return object of type BaseClass and it can not be cast to ExtendedClass. You need to implement type recognition feature in your deserialiser. In your case, returned type depends from properties JSON payload contains. If JSON payload contains extendedString property you know you need to return ExtendedClass in other case just return BaseClass. Your deserialiser could look like below:
class BaseClassDeserializer extends StdDeserializer<BaseClass> {
public BaseClassDeserializer() {
super(BaseClass.class);
}
#Override
public BaseClass deserialize(JsonParser jsonParser, DeserializationContext deserializationContext) throws IOException {
ObjectNode root = jsonParser.readValueAsTree();
List<String> names = getNames(root);
BaseClass result = findAndInitCustomType(names, root);
result = initBase(names, result, root);
initCustomFields(names, root, result);
return result;
}
private void initCustomFields(List<String> names, ObjectNode root, BaseClass result) {
for (String name : names) {
result.putCustomFields(name, root.get(name));
}
}
private BaseClass findAndInitCustomType(List<String> names, ObjectNode root) {
final String extendedString = "extendedString";
if (names.contains(extendedString)) {
ExtendedClass result = new ExtendedClass();
result.setExtendedString(root.get(extendedString).asText());
names.remove(extendedString);
return result;
}
// else - check other custom fields for another types.
// if not available return null
return null;
}
private BaseClass initBase(List<String> names, BaseClass baseClass, ObjectNode root) {
if (baseClass == null) {
baseClass = new BaseClass();
}
final String baseInt = "baseInt";
if (names.contains(baseInt)) {
baseClass.setBaseInt(root.get(baseInt).asInt());
names.remove(baseInt);
}
return baseClass;
}
private List<String> getNames(ObjectNode root) {
List<String> names = new ArrayList<>();
root.fieldNames().forEachRemaining(names::add);
return names;
}
}
Example usage:
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.databind.DeserializationContext;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
import com.fasterxml.jackson.databind.deser.std.StdDeserializer;
import com.fasterxml.jackson.databind.node.JsonNodeFactory;
import com.fasterxml.jackson.databind.node.ObjectNode;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
public class JsonApp {
public static void main(String[] args) throws Exception {
String baseJson = "{"
+ "\"baseInt\": 1,\n"
+ "\t\"customObject\" : {\n"
+ "\t\t\"key\": \"value\"\n"
+ "\t},\n"
+ "\t\"customString\" : \"STRING\""
+ "}";
String extendedJson = "{"
+ "\t\"baseInt\": 1,\n"
+ "\t\"customObject\" : {\n"
+ "\t\t\"key\": \"value\"\n"
+ "\t},\n"
+ "\t\"customString\" : \"STRING\",\n"
+ "\t\"extendedString\" : \"STRING\"\n"
+ "}";
ObjectMapper mapper = new ObjectMapper();
System.out.println(mapper.readValue(baseJson, BaseClass.class));
System.out.println(mapper.readValue(extendedJson, BaseClass.class));
System.out.println(mapper.readValue(extendedJson, ExtendedClass.class));
}
}
Above code prints:
BaseClass{customFields={"customObject":{"key":"value"},"customString":"STRING"}, baseInt=1}
ExtendedClass{extendedString='STRING'} BaseClass{customFields={"customObject":{"key":"value"},"customString":"STRING"}, baseInt=1}
ExtendedClass{extendedString='STRING'} BaseClass{customFields={"customObject":{"key":"value"},"customString":"STRING"}, baseInt=1}
Improvements:
In BaseClass class instead of ObjectNode use Map<String, JsonNode> or even Map<String, Object>. It is not a good idea to tie POJO classes with 3-rd party libraries.
You do not need to use #JsonProperty annotation if you manually handle deserialisation.

How to extract array values within nested JSON data

I have JSON data in the following format:
{
"data": {
"id": 14810798216415,
"name": "crescentbahuman.com",
"is_organization": true,
"email_domains": [
"crescentbahuman.com"
]
}
}
I want to get the string in the "email_domains" field. I wrote the following code as my attempt:
JSONObject dataObject2= (JSONObject)jsonObject2.get("data");
long id = (long) dataObject2.get("id");
System.out.println("worksapce id is: " + id);
String name = (String) dataObject2.get("name");
System.out.println("The worksapce name is: " + name);
boolean is_organization = (boolean) dataObject2.get("is_organization");
System.out.println("The workspace is organization: " + is_organization);
JSONArray email_domains = (JSONArray) jsonObject2.get("email_domains");
Iterator<String> iterator = email_domains.iterator();
while (iterator.hasNext()) {
System.out.println(iterator.next());
}
In this code to get "email_domains" only, a JSON Array object is created that get data from the JSON object and then its iterator is used to get values from within the array. However, it throws a NullPointerException on this line:
Iterator<String> iterator = email_domains.iterator();
I am stuck due to this problem. Can anyone kindly suggest a solution?
If you are using the JSON library from http://www.json.org/java/, then you should not be using JSONObject.get() so frequently. The library has other methods to get specific types, such as getLong(), getJSONArray(), and so forth. For your case with the "email_domains" field, you should try:
JSONArray array = dataObject2.getJSONArray("email_domains");
String value = array.getString(0);
org.json.JSONArray email_domains = (org.json.JSONArray) json.get("email_domains");
int length = email_domains.length();
for(int i = length-1; i > 0; i--) {
org.json.JSONObject jsonData = (org.json.JSONObject) email_domains.get(i);
System.out.println(jsonData);
}
My solution? I hate to be someone to offer a solution in another library... but look into google collections and the Gson helper. It can turn your Json into a map for you, and then back to json again when you are done.
Map map = gson.fromJson(jsonText, Map.class);
JsonArray's can then be cast into List's
try this to fetch "email_domains"
JSONArray email_domains = ((JSONArray) jsonObject).get("email_domains");
or
JSONObject obj = new JSONObject(jsonObject.Tostring());
JSONArray email_domains = obj.optJSONArray("email_domains");
"email_address" is JSONArray so we need to fetch this like
JSONArray email_domains = (JSONArray) dataObject2.getJSONArray("email_domains");
email_domains.get(0); // this will return crescentbahuman.com
Use this implementation
import java.lang.reflect.Field;
import java.util.List;
import com.google.gson.ExclusionStrategy;
import com.google.gson.FieldAttributes;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
public class JsonConvertor {
private static GsonBuilder gsonBuilder;
private static Gson gson;
private JsonConvertor() {
}
public static Object fromJson(String json, Class clz)
{
gson=new Gson();
return gson.fromJson(json,clz);
}
public static String toJson(Object obj) {
gsonBuilder = new GsonBuilder();
gsonBuilder = gsonBuilder
.addSerializationExclusionStrategy(new CustomIclusionStrategy(
obj.getClass()));
gson = gsonBuilder.create();
String json = gson.toJson(obj);
return json;
}
}
class CustomIclusionStrategy implements ExclusionStrategy {
private Class classToIclude;
private Field[] declaredFields;
private List<FieldAttributes> fields;
public CustomIclusionStrategy(List<FieldAttributes> fields) {
this.fields = fields;
}
public CustomIclusionStrategy(Class classToIclude) {
this.classToIclude = classToIclude;
this.declaredFields=classToIclude.getDeclaredFields();
}
// called only if shouldSkipClass returns false
#Override
public boolean shouldSkipField(FieldAttributes f) {
try {
classToIclude.getSuperclass().getDeclaredField(f.getName());
System.out.println(f.getName());
return true;
} catch (Exception e) {
}
return false;
}
#Override
public boolean shouldSkipClass(Class<?> clazz) {
// if returns false shouldSkipField will be called, otherwise
//shouldSkipField will not be called
return false;
}
}
public class Org {
private Data data;
public Org(Data data) {
super();
this.data = data;
}
public Data getData() {
return data;
}
public void setData(Data data) {
this.data = data;
}
public String toJson()
{
return JsonConvertor.toJson(this);
}
public static void main(String[] args) {
String json="{\"data\": {\"id\":\"1\",\"name\":\"org1\",\"is_organization\":true,\"email_domains\": [\"email1\",\"email2\",\"email3\",\"email4\"]}}";
Org o=(Org) JsonConvertor.fromJson(json, Org.class);
System.out.println(o.getData().getEmail_domains());
}
}

GSON parse array of array into Java array of object

I have an JSON array of arrays where none of the elements are named (it's just a pure array)
[
["http://test.com/","rj76-22dk"],
["http://othertest.com/","v287-28n3"]
]
In Java, I'd like to parse this JSON into an array of connectionobjs, where the connectionobj class looks like this:
public static class connectionOptions {
String URL, RESID;
}
I looked through the GSON documentation, but couldn't seem to find anything pertinent to parsing a JSON array into anything other than another Java Array. I want to parse the JSON array into a Java Object, not an array.
Is there a way to do this using Google's GSON?
I don't recommend this at all. You should try to have appropriate JSON that maps correctly to Pojos.
If you can't change your JSON format, you'll need to register a custom TypeAdapter that can do the conversion. Something like
class ConnectionOptionsTypeAdapter extends TypeAdapter<ConnectionOptions> {
#Override
public void write(JsonWriter out, ConnectionOptions value)
throws IOException {
// implement if you need it
}
#Override
public ConnectionOptions read(JsonReader in) throws IOException {
final ConnectionOptions connectionOptions = new ConnectionOptions();
in.beginArray();
connectionOptions.URL = in.nextString();
connectionOptions.RESID = in.nextString();
in.endArray();
return connectionOptions;
}
}
Then just register it
GsonBuilder gsonBuilder = new GsonBuilder().registerTypeAdapter(
ConnectionOptions.class, new ConnectionOptionsTypeAdapter());
Gson gson = gsonBuilder.create();
and use it.
Deserialize your JSON as an ConnectionOptions[] or List<ConnectionOptions>.
I've change your class name to ConnectionOptions to follow Java naming conventions.
You should give a customized Deserializer.
import com.google.gson.*;
import com.google.gson.reflect.TypeToken;
import java.lang.reflect.Type;
import java.util.Collection;
public class TestGson {
public static class ConnectionOptions {
String URL, RESID;
#Override
public String toString() {
return "ConnectionOptions{URL='" + URL + "', RESID='" + RESID + "'}";
}
}
private static class ConnOptsDeserializer implements JsonDeserializer<ConnectionOptions> {
#Override
public ConnectionOptions deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException {
ConnectionOptions connOpts = new TestGson.ConnectionOptions();
JsonArray array = json.getAsJsonArray();
connOpts.URL = array.get(0).getAsString();
connOpts.RESID = array.get(1).getAsString();
return connOpts;
}
}
public static void main(String[] args) {
String json = "[[\"http://test.com/\",\"rj76-22dk\"],\n" +
" [\"http://othertest.com/\",\"v287-28n3\"]]";
GsonBuilder gsonb = new GsonBuilder();
gsonb.registerTypeAdapter(ConnectionOptions.class, new ConnOptsDeserializer());
Gson gson = gsonb.create();
Type collectionType = new TypeToken<Collection<ConnectionOptions>>(){}.getType();
Collection<ConnectionOptions> connList = gson.fromJson(json, collectionType);
System.out.println("connList = " + connList);
}
}

Categories

Resources