Make GSON deserialize numbers to integers or doubles - java

I am having a hard time with GSON.
I have a simple JSON that I want to deserialize to a Map<String,Object>.
It's really intuitive to me that 123 should be parsed as an int (or long), 123.4 as a float( or double).
GSON on the other hand creates Doubles all the time.
Can I tell GSON to not abuse double all the time?
My actual code:
Type mapType = new TypeToken<Map<String, Object>>() {}.getType();
GSON gson = new Gson();
Map<String, Object> map = gson.fromJson(someString, mapType);

The following code compiles & works:
package test;
import java.lang.reflect.Type;
import java.text.NumberFormat;
import java.util.ArrayList;
import java.util.Arrays;
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.JsonParseException;
import com.google.gson.reflect.TypeToken;
public class Main {
public static void main(String[] args) {
GsonBuilder builder = new GsonBuilder();
builder.registerTypeAdapter(Object.class, new MyObjectDeserializer());
Gson gson = builder.create();
String array = "[1, 2.5, 4, 5.66]";
Type objectListType = new TypeToken<ArrayList<Object>>() {}.getType();
List<Object> obj = gson.fromJson(array, objectListType);
System.out.println(Arrays.toString(obj.toArray()));
}
public static class MyObjectDeserializer implements JsonDeserializer<Object> {
public Object deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context)
throws JsonParseException {
Number num = null;
try {
num = NumberFormat.getInstance().parse(json.getAsJsonPrimitive().getAsString());
} catch (Exception e) {
//ignore
}
if (num == null) {
return context.deserialize(json, typeOfT);
} else {
return num;
}
}
}
}
My solution will first try to parse the string as a number, if that fails it will let the standard Gson deserializer do the work.
If you need a number parser that is not locale specific use this method to parse a number:
private static Number parse(String str) {
Number number = null;
try {
number = Float.parseFloat(str);
} catch(NumberFormatException e) {
try {
number = Double.parseDouble(str);
} catch(NumberFormatException e1) {
try {
number = Integer.parseInt(str);
} catch(NumberFormatException e2) {
try {
number = Long.parseLong(str);
} catch(NumberFormatException e3) {
throw e3;
}
}
}
}
return number;
}

It's not a good aproach to mix types like this (integers with doubles). Since you are using Object as a type, you won't be able to get both Integers and Doubles from the map. Gson decides which type is more apropriate for Object. In your case it is Double, because all values CAN BE doubles, but all values CAN'T BE integers.
If you really need to mix types, try to use Number class instead of Object. Example:
public static void main(String[] args){
String array = "[1, 2.5, 4, 5.66]";
Gson gson = new Gson();
Type type = new TypeToken<ArrayList<Number>>() {}.getType();
List<Number> obj = gson.fromJson(array, type);
System.out.println(Arrays.toString(obj.toArray()));
}
Output: [1, 2.5, 4, 5.66]
While this:
public static void main(String[] args){
String array = "[1, 2.5, 4, 5.66]";
Gson gson = new Gson();
Type type = new TypeToken<ArrayList<Object>>() {}.getType();
List<Object> obj = gson.fromJson(array, type);
System.out.println(Arrays.toString(obj.toArray()));
}
will give you output: [1.0, 2.5, 4.0, 5.66]

If you are not bound to specifically use gson library you can solve this deserializing using jackson one as follow:
new ObjectMapper().readValue(yourJson, new TypeReference<Map<String,Object>>() {});
Here a complete junit test that expose the differences between the two libraries deserializing an integer value:
#Test
public void integerDeserializationTest() throws Exception {
final String jsonSource = "{\"intValue\":1,\"doubleValue\":2.0,\"stringValue\":\"value\"}";
//Using gson library "intValue" is deserialized to 1.0
final String gsonWrongResult = "{\"intValue\":1.0,\"doubleValue\":2.0,\"stringValue\":\"value\"}";
Map<String,Object> gsonMap = new Gson().fromJson(jsonSource, new TypeToken<Map<String, Object>>() {
}.getType());
assertThat(new Gson().toJson(gsonMap),is(gsonWrongResult));
//Using jackson library "intValue" is deserialized to 1
Map<String,Object> jacksonMap = new ObjectMapper().readValue(jsonSource, new TypeReference<Map<String,Object>>() {});
assertThat(new ObjectMapper().writeValueAsString(jacksonMap),is(jsonSource));
}

You can do the following changes in order to parse that data:
testData1={"GOAL1":123, "GOAL2":123.45,"GOAL5":1256,"GOAL6":345.98}
and below is your code to actually parse it.
Type mapType = new TypeToken<Map<String, Object>>() {
}.getType();
String str = prop.getProperty("testData1");
System.out.println(str);
Gson gson = new Gson();
Map<String, Object> map = gson.fromJson(str, mapType);
for (String key : map.keySet()) {
String a = null;
try {
if (map.get(key) instanceof Double) {
a = "" + map.get(key);
} else {
if (map.get(key) instanceof String) {
a = (String) map.get(key);
} else {
a = null;
}
}
if (a != null && a.contains(".") && !a.endsWith(".0")) {
// Convert it into Double/Float
} else {
// Work as Integer
}
} catch (Exception ex) {
ex.printStackTrace();
}
}

Related

How to avoid string to number conversion for doubles and numbers expressed in Scientific Notation?

I receive JSON payload which is set of key-value pairs. Value may be either string or number. I have to parse the JSON and store key-value pairs into appropriate varchar2 columns. I should save incoming number exactly as it was presented in the input payload.
But for numbers presented like 1.1E4, 0.00000000000003 and similar I get 11000.0, 3.0E-14 instead.
Is it a way to disable/prevent number conversion to have just string representation instead?
I use FasterXML Jackson implementation.
By the way there is no actual doc available - all sources I found point to http://wiki.fasterxml.com/JacksonHome which is down right now.
I found two similar questions here
Jackson JSON converts integers into strings
Disable the Number to String automatic conversion in jackson
but both require exception when encounter number, which is not my case. I have tried suggested solutions but was unsuccessful in modifying them to fit my task.
Also there is no answer in
https://github.com/FasterXML/jackson-databind/issues/796
Right now I have no specification for input string other than key-value pairs. So just an example:
I may receive something like:
{"a":"text", "b":"35", "c":{"d":"another"}, "e":["array",35], "f":1.1E4, "g":0.00000000000003}
I want string pairs
"a" -> "text", "b" -> "35", "c" -> "{\"d\":\"another\"}", "e" -> "[\"array\",35]", "f" -> "1.1E4"
The simplest conversion way is:
public void test() throws IOException {
Map map = new ObjectMapper().readValue(
"{\"a\":\"text\", \"b\":\"35\", \"c\":{\"d\":\"another\"}, \"e\":[\"array\",35], \"f\":1.1E4, \"g\":0.00000000000003}"
, Map.class);
System.out.println(map);
}
results in:
{a=text, b=35, c={d=another}, e=[array, 35], f=11000.0, g=3.0E-14}
The more accurate way:
public class JsonUtil2 {
private static final ObjectMapper OBJECT_MAPPER = new ObjectMapper();
public static Map<String, String> parse(String json) throws IOException {
ObjectNode objectNode = (ObjectNode) OBJECT_MAPPER.readTree(json);
Map<String, String> result = new HashMap<>(objectNode.size());
objectNode.fields().forEachRemaining(entry -> result.put(entry.getKey(), toJson(entry.getValue())));
return result;
}
private static String toJson(JsonNode jsonNode) {
if (jsonNode.isNumber()) {
if (jsonNode instanceof DoubleNode || jsonNode instanceof FloatNode) {
DecimalFormatSymbols dfs = new DecimalFormatSymbols();
dfs.setDecimalSeparator('.');
dfs.setMinusSign('-');
DecimalFormat df = new DecimalFormat("#.#", dfs);
df.setMaximumFractionDigits(32);
df.setMaximumIntegerDigits(32);
return df.format(jsonNode.doubleValue());
} else {
return jsonNode.asText();
}
} else if (jsonNode.isValueNode()) {
return jsonNode.asText();
} else {
try {
return OBJECT_MAPPER.writeValueAsString(jsonNode);
} catch (JsonProcessingException e) {
throw new RuntimeException(e);
}
}
}
}
which results in:
{a=text, b=35, c={"d":"another"}, e=["array",35], f=11000, g=0.00000000000003}
This is much better, but still differ in f=11000 instead of f=1.1E4.
In your case you want to treat everything as a String, so you need a custom deserialiser which reads JSON Object and JSON Array as String. We can also force Jackson to read Map<String, String> by providing this information using TypeFactory.
Assume our JSON payload looks like below:
{
"a": "text",
"b": "35",
"c": {
"d": "another",
"dd":3.44E3
},
"e": [
"array",
35,
2.3E5
],
"f": 1.1E4,
"g": 0.00000000000003
}
Example code:
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.core.JsonToken;
import com.fasterxml.jackson.databind.DeserializationContext;
import com.fasterxml.jackson.databind.JsonDeserializer;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.deser.std.StringDeserializer;
import com.fasterxml.jackson.databind.module.SimpleModule;
import com.fasterxml.jackson.databind.type.CollectionType;
import com.fasterxml.jackson.databind.type.MapType;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
public class JsonTreeApp {
public static void main(String[] args) throws Exception {
File jsonFile = new File("./resource/test.json").getAbsoluteFile();
SimpleModule everythingIsStringModule = new SimpleModule();
everythingIsStringModule.addDeserializer(String.class, new EverythingIsStringDeserializer());
ObjectMapper mapper = new ObjectMapper();
mapper.registerModule(everythingIsStringModule);
MapType mapType = mapper.getTypeFactory().constructMapType(LinkedHashMap.class, String.class, String.class);
LinkedHashMap<String, String> map = mapper.readValue(jsonFile, mapType);
map.forEach((k, v) -> System.out.println(k + " => " + v));
}
}
class EverythingIsStringDeserializer extends StringDeserializer {
#Override
public String deserialize(JsonParser p, DeserializationContext ctxt) throws IOException {
if (p.currentToken() == JsonToken.START_OBJECT) {
return _deserializeFromObject(p, ctxt);
}
return super.deserialize(p, ctxt);
}
private String _deserializeFromObject(JsonParser p, DeserializationContext ctxt) throws IOException {
MapType mapType = ctxt.getTypeFactory().constructMapType(LinkedHashMap.class, String.class, String.class);
JsonDeserializer<Object> deserializer = ctxt.findRootValueDeserializer(mapType);
Map<String, String> map = (Map<String, String>) deserializer.deserialize(p, ctxt);
return toString(map);
}
#Override
protected String _deserializeFromArray(JsonParser p, DeserializationContext ctxt) throws IOException {
CollectionType collectionType = ctxt.getTypeFactory().constructCollectionType(ArrayList.class, String.class);
JsonDeserializer<Object> deserializer = ctxt.findRootValueDeserializer(collectionType);
List<String> list = (List<String>) deserializer.deserialize(p, ctxt);
return toString(list);
}
private String toString(Map<String, String> map) {
StringBuilder builder = new StringBuilder(128);
builder.append('{');
boolean addComa = false;
for (Map.Entry<String, String> entry : map.entrySet()) {
if (addComa) {
builder.append(',');
}
builder.append('"').append(entry.getKey())
.append("\":");
appendValue(entry.getValue(), builder);
addComa = true;
}
builder.append('}');
return builder.toString();
}
private String toString(List<String> list) {
StringBuilder builder = new StringBuilder(128);
builder.append('[');
boolean addComa = false;
for (String item : list) {
if (addComa) {
builder.append(',');
}
appendValue(item, builder);
addComa = true;
}
builder.append(']');
return builder.toString();
}
private void appendValue(String value, StringBuilder builder) {
if (value == null || value.isEmpty()) {
builder.append("\"\"");
return;
}
if (Character.isAlphabetic(value.charAt(0))) {
builder.append('"').append(value).append('"');
} else {
builder.append(value);
}
}
}
Prints:
a => text
b => 35
c => {d=another, dd=3.44E3}
e => [array, 35, 2.3E5]
f => 1.1E4
g => 0.00000000000003

Deserialization fails with when using null as map key

Gson deserialization failed when using null as map key
Gson gson = new GsonBuilder()
.serializeNulls()
.serializeSpecialFloatingPointValues()
.create();
Map<Integer, String> mapData = new HashMap<Integer, String>();
mapData.put(null, "abc");
String data = gson.toJson(mapData);
System.out.println(data);
Type type = TypeToken.getParameterized(HashMap.class, Integer.class, String.class).getType();
Object obj = gson.fromJson(data, type);
System.out.println(obj);
Exception:
Exception in thread "main" com.google.gson.JsonSyntaxException: java.lang.NumberFormatException: For input string: "null"
The following code snippet work well.
Gson gson = new GsonBuilder()
.serializeNulls()
.serializeSpecialFloatingPointValues()
.create();
Map<String, Integer> mapData = new HashMap<>();
mapData.put("abc", null);
String data = gson.toJson(mapData);
System.out.println(data);
Type type = TypeToken.getParameterized(HashMap.class, String.class, Integer.class).getType();
Object obj = gson.fromJson(data, type);
System.out.println(obj);
output:
{"abc":null}
{abc=null}
You can create TypeAdapter for Integer:
Gson gson = new GsonBuilder()
.serializeNulls()
.serializeSpecialFloatingPointValues()
.registerTypeAdapter(Integer.class, new TypeAdapter<Integer>() {
#Override
public void write(JsonWriter jsonWriter, Integer integer) throws IOException {
jsonWriter.jsonValue(String.valueOf(integer));
}
#Override
public Integer read(JsonReader jsonReader) throws IOException {
if (jsonReader.peek() == JsonToken.NULL) {
jsonReader.nextNull();
return null;
} else {
String numberStr = jsonReader.nextString();
return "null".equals(numberStr) ? null : Integer.valueOf(numberStr);
}
}
})
.create();
Output:
{"null":"abc"}
{null=abc}
The reason why null works for value and doesn't works for key without custom Integer Adapter, is the way map adapter serialises each: key all the time is String in JSON, it couldn't be neither int or null. You can see it in your output: {"null":"abc"} vs {"abc":null}.
Check out implementation of map type adapter.

How to create array in json object

I am using something like this-
String Number1=to_1;
String Number2=to_2;
String[] arrayNumbers = new String[] {(char)34+Number1+(char)34,(char)34+Number2+(char)34};
System.out.println(Arrays.toString(arrayNumbers));
JSONObject jsonObj = new JSONObject();
jsonObj.put("to",Arrays.toString(arrayNumbers));
jsonObj.put("type",type);
jsonObj.put("callback",callbackUrl);
JSONArray array = new JSONArray();
JSONObject Array_item = new JSONObject();
jsonObj.put(type, array);
Array_item.put("caption",captionName);
array.add(Array_item);
System.out.println(jsonObj.toString());
Expected-
{
"to":["91890xx", "91890xx"],
"type": "document", "document" : {"caption" : "doc"},
"callback":"{{callback}}"
}
Actual- {"document":[{"caption":"hello"}],"callback":"{{callback}}","to":"[\"91890xxx\", \"91890xx\"]","type":"document"}
I don't know any more logic to remove the out double quotes of to number where as its considering that to a string where as both the numbers should be array format as mentioned in Expected.
First, I show you the correct codes:
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
public class Test {
/**
* <pre>
* {
* "to":["91890xx", "91890xx"],
* "type": "document",
* "document" : {"caption" : "doc"},
* "callback":"{{callback}}"
* }
* </pre>
*
* #param args
* #throws JSONException
*/
public static void main(String[] args) throws JSONException {
String number1 = "91890";
String number2 = "91890";
String[] numbers = new String[]{number1, number2};
JSONArray toNode = new JSONArray();
for (String number : numbers) {
toNode.put(number);
}
JSONObject jsonObj = new JSONObject();
jsonObj.put("to", toNode);
jsonObj.put("type", "document");
jsonObj.put("document", new JSONObject().put("caption", "doc"));
jsonObj.put("callback", "{{callback}}");
System.out.println(jsonObj.toString());
}
}
Result:
{"document":{"caption":"doc"},"callback":"{{callback}}","to":["91890","91890"],"type":"document"}
If you want create a josn array node, you show use JSONArray, and use JSONArray#put(*) method to add elements.
Put string into JSONArray or JSONObject, you don't need wrap the string with quote("). Besides, you should write \" instead of (char) 34 which is a little obscure in Java.
The following case is used to reply comments.
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import java.math.BigDecimal;
import java.net.URI;
import java.net.URL;
import java.util.*;
public class Test1 {
public static void main(String[] args) throws JSONException {
Map<String, Object> map = new HashMap<>();
map.put("key1", "value1");
map.put("key2", new Date());
map.put("key3", 1);
map.put("key4", null);
map.put("key5", Collections.singletonMap("key5-key1", "value"));
map.put("key6", Arrays.asList(1, 2, 3, 4));
map.put("key7", BigDecimal.TEN);
map.put("key8", new String[]{"a", "b", "c"});
map.put("key9", TestEnum.A);
map.put("key10", new TestEnum[]{TestEnum.A, TestEnum.B, TestEnum.C});
Object json = buildJsonObj(map);
System.out.println(json);
}
private static Object buildJsonObj(Object source) throws JSONException {
if (source == null) {
return null;
}
if (isSimpleValueType(source.getClass())) {
return source;
}
if (source instanceof Map) {
Map<Object, Object> map = (Map<Object, Object>) source;
JSONObject jsonObject = new JSONObject();
for (Map.Entry<Object, Object> entry : map.entrySet()) {
Object key = entry.getKey();
if (!(key instanceof String)) {
throw new IllegalArgumentException("key must be string.");
}
jsonObject.put((String) key, buildJsonObj(entry.getValue()));
}
return jsonObject;
}
if (source instanceof Iterable) {
Iterable<Object> iterable = (Iterable<Object>) source;
JSONArray jsonArray = new JSONArray();
for (Object value : iterable) {
jsonArray.put(buildJsonObj(value));
}
return jsonArray;
}
if (source.getClass().isArray()) {
Object[] array = (Object[]) source;
JSONArray jsonArray = new JSONArray();
for (Object value : array) {
jsonArray.put(buildJsonObj(value));
}
return jsonArray;
}
throw new IllegalArgumentException("Unsupported type: " + source + ".");
}
private static boolean isSimpleValueType(Class<?> clazz) {
return (Enum.class.isAssignableFrom(clazz) ||
CharSequence.class.isAssignableFrom(clazz) ||
Number.class.isAssignableFrom(clazz) ||
Date.class.isAssignableFrom(clazz) ||
URI.class == clazz || URL.class == clazz ||
Locale.class == clazz);
}
public enum TestEnum {
A, B, C
}
}
Result:
{"key1":"value1","key2":"Thu Mar 14 20:20:49 CST 2019","key5":{"key5-key1":"value"},"key6":[1,2,3,4],"key3":1,"key9":"A","key7":10,"key8":["a","b","c"],"key10":["A","B","C"]}
Replace line
jsonObj.put("to",Arrays.toString(arrayNumbers));
to
jsonObj.put("to", Arrays.asList(arrayNumbers));
Your value is String of 'to',so can you please convert string to json after that you can convert json to array.
Replace
jsonObj.put("to",Arrays.toString(arrayNumbers));
With
jsonObj.put("to",arrayNumbers);
let a = {"document":[{"caption":"hello"}],"callback":"{{callback}}","to":"[\"91890xxx\", \"91890xx\"]","type":"document"};
a = JSON.parse(JSON.stringify(a));
let b = JSON.parse(a.to);
a.to = b;
console.log(a);
This is the exact structure of you required json.
{
"to": ["91890xx", "91890xx"],
"type": "document",
"document": {
"caption": "doc"
},
"callback": "{{callback}}"
}
To Remove the double quotes, please use replace function as shown below
String Number1="[\"91890xxx\", \"91890xx\"]";
Number1.replace("\\/", "");
System.out.println("Excepted output:"+Number1);
output:
Excepted output:["91890xxx", "91890xx"]

How to create a Java method to parse a big JSON file

I am trying to parse the content of JSON file text.json by using Jackson library.
What I want is to make a java method to get all keys and values of it, but so far in my code I get only the first key and the first value of the JSON file.
The code snippet I used as guidance to make my own Java class is the following:
public void parse(String json) {
JsonFactory factory = new JsonFactory();
ObjectMapper mapper = new ObjectMapper(factory);
JsonNode rootNode = mapper.readTree(json);
Iterator<Map.Entry<String,JsonNode>> fieldsIterator = rootNode.fields();
while (fieldsIterator.hasNext()) {
Map.Entry<String,JsonNode> field = fieldsIterator.next();
System.out.println("Key: " + field.getKey() + "\tValue:" + field.getValue());
}
}
And my Java class that I created is shown below:
import java.io.File;
import java.io.IOException;
import java.util.Iterator;
import java.util.Map;
import org.codehaus.jackson.JsonFactory;
import org.codehaus.jackson.JsonGenerationException;
import org.codehaus.jackson.JsonNode;
import org.codehaus.jackson.JsonParser;
import org.codehaus.jackson.map.JsonMappingException;
import org.codehaus.jackson.map.ObjectMapper;
public class JacksonStreamExample {
public static void main(String[] args) {
try {
//Create a JsonFactory instance
JsonFactory factory = new JsonFactory();
//Create a JsonParser instance to read from file c:\\text.json
JsonParser jParser = factory.createJsonParser(new File("c:\\text.json"));
/*Create an ObjectMapper instance to provide a pointer
* to root node of the tree after reading the JSON
*/
ObjectMapper mapper = new ObjectMapper(factory);
//Create tree from JSON
JsonNode rootNode = mapper.readTree(jParser);
Iterator<Map.Entry<String,JsonNode>> fieldsIterator = rootNode.getFields();
while (fieldsIterator.hasNext()) {
Map.Entry<String,JsonNode> field = fieldsIterator.next();
System.out.println("Key: " + field.getKey() + "\tValue:" + field.getValue());
}
jParser.close();
} catch (JsonGenerationException e) {
e.printStackTrace();
} catch (JsonMappingException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
}
My Eclipse output is the following which creates only 1 pair(key-value):
Key: cells Value:[{"type":"basic.Circle","size":{"width":90,"height":54},"position":{"x":-80,"y":200},"angle":0,"id":"cae4c219-c2cd-4a4b-b50c-0f269963ca24","embeds":"","z":1,"wi_name":"START","wi_displayName":"START","wi_description":"","wi_join":"<None>","wi_split":"<None>","wi_performingRole":"<None>","wi_expected_activity_time":null,"wi_expected_user_time":null,"wi_maximum_activity_time":null,"wi_initial_delay":null,"wi_time_unit":"Seconds","wi_required_transitions_for_AND_JOIN":null,"wi_custom_page":"","attrs":{"circle":{"fill":"#000000","width":50,"height":30,"stroke-width":1,"stroke-dasharray":"0"},"text":{"font-size":10,"text":"START","fill":"#ffffff","font-family":"Arial","stroke":"#000000","stroke-width":0,"font-weight":400}}},{"type":"basic.Circle","size":{"width":90,"height":54},"position":{"x":210,"y":200},"angle":0,"id":"d23133e0-e516-4f72-8127-292545d3d479","embeds":"","z":2,"wi_name":"END","wi_displayName":"END","wi_description":"","wi_join":"<None>","wi_split":"<None>","wi_performingRole":"<None>","wi_expected_activity_time":null,"wi_expected_user_time":null,"wi_maximum_activity_time":null,"wi_initial_delay":null,"wi_time_unit":"Seconds","wi_required_transitions_for_AND_JOIN":null,"wi_custom_page":"","attrs":{"circle":{"fill":"#000000","width":50,"height":30,"stroke-width":1,"stroke-dasharray":"0"},"text":{"font-size":10,"text":"END","fill":"#ffffff","font-family":"Arial","stroke":"#000000","stroke-width":0,"font-weight":400}}},{"type":"basic.Rect","position":{"x":-80,"y":370},"size":{"width":90,"height":54},"angle":0,"id":"a53898a5-c018-45c4-bd3f-4ea4d69f58ed","embeds":"","z":3,"wi_name":"ACTIVITY_1","wi_displayName":"ACTIVITY 1","wi_description":"","wi_join":"<None>","wi_split":"<None>","wi_performingRole":"<None>","wi_expected_activity_time":null,"wi_expected_user_time":null,"wi_maximum_activity_time":null,"wi_initial_delay":null,"wi_time_unit":"Seconds","wi_required_transitions_for_AND_JOIN":null,"wi_custom_page":"","attrs":{"rect":{"width":50,"height":30,"rx":2,"ry":2,"stroke-width":1,"stroke-dasharray":"0"},"text":{"text":"Activity","font-size":10,"font-family":"Arial","stroke":"#000000","stroke-width":0,"font-weight":400}}},{"type":"basic.Rect","position":{"x":220,"y":370},"size":{"width":90,"height":54},"angle":0,"id":"e2bd21f2-508d-44b9-9f68-e374d4fa87ea","embeds":"","z":4,"wi_name":"ACTIVITY_2","wi_displayName":"ACTIVITY 2","wi_description":"","wi_join":"<None>","wi_split":"<None>","wi_performingRole":"<None>","wi_expected_activity_time":null,"wi_expected_user_time":null,"wi_maximum_activity_time":null,"wi_initial_delay":null,"wi_time_unit":"Seconds","wi_required_transitions_for_AND_JOIN":null,"wi_custom_page":"","attrs":{"rect":{"width":50,"height":30,"rx":2,"ry":2,"stroke-width":1,"stroke-dasharray":"0"},"text":{"text":"Workitem","font-size":10,"font-family":"Arial","stroke":"#000000","stroke-width":0,"font-weight":400}}},{"type":"link","source":{"id":"cae4c219-c2cd-4a4b-b50c-0f269963ca24"},"target":{"id":"d23133e0-e516-4f72-8127-292545d3d479"},"router":{"name":"manhattan"},"labels":[{"position":0.5,"attrs":{"text":{"text":"Name"}}}],"id":"60ee7ff7-3a3b-487d-b581-49027e7bebe4","embeds":"","z":5,"attrs":{".marker-source":{"d":"M 10 0 L 0 5 L 10 10 z","transform":"scale(0.001)"},".marker-target":{"d":"M 10 0 L 0 5 L 10 10 z"},".connection":{"stroke":"black"}}},{"type":"link","source":{"id":"a53898a5-c018-45c4-bd3f-4ea4d69f58ed"},"target":{"id":"e2bd21f2-508d-44b9-9f68-e374d4fa87ea"},"router":{"name":"manhattan"},"labels":[{"position":0.5,"attrs":{"text":{"text":"Name"}}}],"id":"cea0d1c2-2c18-4bd7-ba35-d94918c6fc9b","embeds":"","z":6,"attrs":{".marker-source":{"d":"M 10 0 L 0 5 L 10 10 z","transform":"scale(0.001)"},".marker-target":{"d":"M 10 0 L 0 5 L 10 10 z"},".connection":{"stroke":"black"}}}]
I just need to make a method to put this code inside to get all key-value pairs:
//Create a JsonFactory instance
JsonFactory factory = new JsonFactory();
//Create a JsonParser instance to read from file c:\\text.json
JsonParser jParser = factory.createJsonParser(new File("c:\\text.json"));
/*Create an ObjectMapper instance to provide a pointer
*to root node of the tree after reading the JSON
*/
ObjectMapper mapper = new ObjectMapper(factory);
//Create tree from JSON
JsonNode rootNode = mapper.readTree(jParser);
Iterator<Map.Entry<String,JsonNode>> fieldsIterator = rootNode.getFields();
while (fieldsIterator.hasNext()) {
Map.Entry<String,JsonNode> field = fieldsIterator.next();
System.out.println("Key: " + field.getKey() + "\tValue:" + field.getValue());
}
How will I do it please?
You should use Jackson2 instead of Jackson1. As the error message specifies, Jackson1's JsonNode indeed does not have this fields() method, while Jackson2's version does.
In Jackson1, you would have to do something like this:
Iterator<String> fieldNameIterator = rootNode.getFieldNames();
while (fieldNameIterator.hasNext()) {
String fieldName = fieldNameIterator.next();
JsonNode fieldValue = rootNode.getFieldValue(fieldName);
System.out.println("Key: " + fieldName + "\tValue:" + fieldValue);
}
I solved my problem by changing JSON library.
I used json-simple-1.1.1
My final code that worked is the following:
package jsontoxml;
import java.io.*;
import org.json.simple.parser.JSONParser;
import org.json.simple.*;
import java.util.*;
public class JacksonStreamExample {
public static void main(String[] args) {
JSONParser parser = new JSONParser();
try {
Object obj = parser.parse(new FileReader("text.json"));
JSONObject jsonObject = (JSONObject) obj;
JSONArray cells = (JSONArray) jsonObject.get("cells");
Iterator<JSONObject> iterator = cells.iterator();
while(iterator.hasNext()){
System.out.println(iterator.next());
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
Now that you have modified the question and json, here is the solution you're looking for:
public static void main(String[] args) {
try {
Map source = (new ObjectMapper()).readValue(new File(
"C:\\json.txt"), Map.class);
Map<String, Object> result = new LinkedHashMap<>();
buildFlattenedMap(result, source, null);
for (String s : result.keySet()) {
System.out.println(s);
}
} catch (Exception e) {
e.printStackTrace();
}
}
private static void buildFlattenedMap(Map<String, Object> result,
Map<String, Object> source, String path) {
for (Map.Entry<String, Object> entry : source.entrySet()) {
String key = entry.getKey();
if (hasText(path)) {
if (key.charAt(0) == '[') {
key = StringUtils.join(path, key);
} else {
key = StringUtils.join(path, '.', key);
}
}
Object value = entry.getValue();
if (value instanceof String) {
result.put(key, value);
} else if (value instanceof Map) {
// Need a compound key
#SuppressWarnings("unchecked")
Map<String, Object> map = (Map<String, Object>) value;
buildFlattenedMap(result, map, key);
} else if (value instanceof Collection) {
// Need a compound key
#SuppressWarnings("unchecked")
Collection<Object> collection = (Collection<Object>) value;
int count = 0;
for (Object object : collection) {
buildFlattenedMap(result, Collections.singletonMap("["
+ (count++) + "]", object), key);
}
} else {
result.put(key, value == null ? StringUtils.EMPTY : value);
}
}
}
private static boolean hasText(CharSequence str) {
if (StringUtils.isEmpty(str)) {
return false;
}
int strLen = str.length();
for (int i = 0; i < strLen; i++) {
if (!Character.isWhitespace(str.charAt(i))) {
return true;
}
}
return false;
}
FYI, buildFlattenedMap method is actually from spring framework which I had used recently for processing a similar stuff.
https://github.com/spring-projects/spring-framework/blob/master/spring-beans/src/main/java/org/springframework/beans/factory/config/YamlProcessor.java

Access nested JSON object value using java

{
"files": {
"f1.png": {
"intext": "A",
"inval": 0,
"inbinary": false
},
"f2.png": {
"intext": "A",
"inval": 0,
"inbinary": true
}
}
}
How to access value of inval when the f1.png value is not fixed. i.e. the name of file can be anything, its not known so how can I access value for inval field for various files in this JSON using Java?
Please try below code,
import org.json.JSONException;
import org.json.JSONObject;
public static void main(String[] args) {
String jsonString = "{\"files\": {\"f1.png\": {\"intext\": \"A\",\"inval\": 0,\"inbinary\": false}, \"f2.png\": {\"intext\": \"A\",\"inval\": 0,\"inbinary\": true}}}";
try {
JSONObject jsonObject =new JSONObject(jsonString);
JSONObject jsonChildObject = (JSONObject)jsonObject.get("files");
Iterator iterator = jsonChildObject.keys();
String key = null;
while(iterator.hasNext()){
key = (String)iterator.next();
System.out.println("inval value: "+((JSONObject)jsonChildObject.get(key)).get("inval"));
}
}
catch (JSONException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
Hope it solves your issue
Using Jackson and JsonNode, you'd do:
private static final ObjectReader READER = new ObjectMapper()
.getReader;
// blah
// read the node
final JsonNode node = READER.readTree(fromWhatever);
// access the inner "files" member
final JsonNode filesNode = node.get("files");
to access the inner object.
Then to walk the filesNode object you'd do:
final Iterator<Map.Entry<String, JsonNode>> iterator = filesNode.fields();
Map.Entry<String, JsonNode> entry;
while (iterator.hasNext()) {
entry = iterator.next();
// the "inval" field is entry.getValue().get("inval")
}
If you can use this project this becomes more simple:
// or .fromFile(), .fromReader(), others
final JsonNode node = JsonLoader.fromString(whatever);
final Map<String, JsonNode> map = JacksonUtils.nodeToMap(node.get("files"));
// walk the map
You can use JsonPath library to access child elements.
https://github.com/json-path/JsonPath
It can be as simple as
List<String> names = JsonPath.read(json, "$.files.*);
With some modifications.
You can use ObjectMapper.
First create a class Image.
import lombok.Data;
#Data
public class Image {
private String intext;
private Integer inval;
private Boolean inbinary;
}
and convert to Map
final ObjectMapper objectMapper = new ObjectMapper();
final String jsonString =
"{\"files\": {\"f1.png\": {\"intext\": \"A\",\"inval\": 0,\"inbinary\": false}, \"f2.png\": {\"intext\": \"A\",\"inval\": 0,\"inbinary\": true}}}";
final Map<String, Map<String, Image>> output =
objectMapper.readValue(
jsonString, new TypeReference<Map<String, Map<String, Image>>>() {});
Thanks.
for nested array type,
{
"combo": [
{"field":"divisions"},
{"field":"lob"}
]
}
below code will help.
Iterator<?> keys = jsnObj.keys();
while(keys.hasNext()) {
String key = (String) keys.next();
JSONArray list = (JSONArray) jsnObj.get(key);
if(list==null || list.length()==0)
return null;
List<String> comboList = new ArrayList<String>();
for(int i=0;i<list.length();i++){
JSONObject jsonObject = (JSONObject)list.get(i);
if(jsonObject==null)
continue;
comboList.add((String)jsonObject.get("fields"));
}
}

Categories

Resources