I have a string like this
{"key0":"value0","key1":"value1","key0":"value3"}
I want to store it in a map and the desired result is {"key0":"value3","key1":"value1"}
Using org.json.JsonObject: I passed the string to the constructor and Duplicate key exception is thrown
Using GSON: Same exception when I tried through new Gson.fromJson(string,Type)
Using Jackson: It does work
Is there a workaround to achieve the same using JSONObject and Gson
Interestingly if you first cast that json to an Object and then to a Map<String,String> your desired result happens:
String json = "{\"key0\":\"value0\",\"key1\":\"value1\",\"key0\":\"value3\"}";
Gson gson = new Gson();
Object obj = gson.fromJson(json, Object.class);
try {
Map<String,String> map = (Map<String, String>)obj;
// Outputs...
// key0=value3
// key1=value1
for (Map.Entry<String,String> entry : map.entrySet()) {
System.out.println(entry.getKey() + "=" + entry.getValue());
}
} catch (ClassCastException e) {
e.printStackTrace();
}
GSON uses MapTypeAdapterFactory to deserialioze map. Below is a short excerpt of its source code where a new entry is put in a map:
V replaced = map.put(key, value);
if (replaced != null) {
throw new JsonSyntaxException("duplicate key: " + key);
}
Knowing that there is at least one way to bypass this strict behavior: create your own map that overrides the method put(..) to return always null, like:
public class DuploMap extends HashMap<String, String>{
#Override
public String put(String key, String value) {
super.put(key, value);
return null;
}
}
then deserailizing to it like:
gson.fromJson(JSON, DuploMap.class);
will not throw that exception.
You can use GSON's JsonReader if you do not mind the manual effort.
On the plus side:
faster (no reflection, no casts)
fully under your control
--
String json = "{"key0":"value0","key1":"value1","key0":"value3"}";
JsonReader jsonReader = new JsonReader(new StringReader(json));
HashMap<String,String> map = new HashMap<String, String>()
String currKey;
try {
while(jsonReader.hasNext()){
JsonToken nextToken = jsonReader.peek();
if(JsonToken.NAME.equals(nextToken)){
currKey = jsonReader.nextName();
}
if(JsonToken.STRING.equals(nextToken)){
map.put(currKey, jsonReader.nextString())
}
}
} catch (IOException e) {
e.printStackTrace();
}
Related
I got a problem when I use gson to parse json file. I want to deserialize some similar json files
to my objects. I typed a method to do this job, but I don't know how to apply this method to different json files. These json files have some similar structures, so I want to deserialize them into subtypes of the same supertypes.
private Map<String, PatternDetectionRequestBody> readRequestFromJson(File jsonFile) {
Map<String, PatternDetectionRequestBody> requestBodyMap = null;
try {
FileReader fileReader = new FileReader(jsonFile);
JsonReader jsonReader = new JsonReader(fileReader);
Gson gson = new Gson();
Type type = new TypeToken<Map<String, PatternDetectionRequestBody>>(){}.getType();
requestBodyMap = gson.fromJson(jsonReader, type);
} catch (FileNotFoundException e) {
e.printStackTrace();
}
return requestBodyMap;
}
As code above, I want to use this code to parse different json files by changing PatternDetectionRequestBody to some sibling classes. Could anyone tell me how to do this?
Can't you just do something like this? Class<? extends ParentOfYourObject>
EDIT
Did something like this for a trial, and it worked.
private static <T> Map<String, T> readRequestFromJson(File jsonFile, TypeToken<Map<String, T>> typeToken) {
Map<String, T> requestBodyMap = null;
try {
FileReader fileReader = new FileReader(jsonFile);
JsonReader jsonReader = new JsonReader(fileReader);
Gson gson = new Gson();
requestBodyMap = gson.fromJson(jsonReader, typeToken.getType());
} catch (FileNotFoundException e) {
e.printStackTrace();
}
return requestBodyMap;
}
public static void main(String[] args) throws Exception {
Map<String, Person> myMap = (Map<String, Person>) readRequestFromJson(new File("C:/Users/User.Admin/Desktop/jsonFile"),
new TypeToken<Map<String, Person>>() {
});
for (Map.Entry<String, Person> entry : myMap.entrySet()) {
System.out.println(entry.getValue().getFirstName());
}
}
I m trying to serialize hibernate object to json with the use of Gson library.I had to implement custom Type Adapter in this case because GSon can't serialize HibernateProxy objects in normal manner.I tried to implement the TypeAdapter as I can use with any object type without modifying it .
Here is my TypeAdapter class :
public class CustomTypeAdapter implements JsonSerializer<Object> {
#Override
public JsonElement serialize(Object object, Type type, JsonSerializationContext jsc) {
JsonObject jsonObject = new JsonObject();
try {
Map<String, String> properties = BeanUtils.describe(object);
//org.apache.commons.beanutils
for (Map.Entry<String, String> entry : properties.entrySet()) {
jsonObject.addProperty(entry.getKey(), entry.getValue());
}
} catch (Exception ex) {
ex.printStackTrace();
}
return jsonObject;
}
}
But the problem I have got is the inner objects are not going to serialize with this implementation. It is just returns the address of the object.(Product#54554356)
List<ProductHasSize> phsList = s.createCriteria(ProductHasSize.class, "phs")
.createAlias("phs.product", "product")
.add(Restrictions.eq("product.id", 1))
.list();
GsonBuilder gsonBuilder = new GsonBuilder();
Gson gson = gsonBuilder.registerTypeAdapter(ProductHasSize.class, new CustomTypeAdapter()).create();
String element = gson.toJson(phsList);
response.getWriter().write(element);
Current Out-put :
[{"product":"com.certus.dbmodel.Product#54554356","size":"com.certus.dbmodel.Size#215a88a","price":"1250.0","qnty":"20","id":"1","class":"class com.certus.dbmodel.ProductHasSize"},{"product":"com.certus.dbmodel.Product#54554356","size":"com.certus.dbmodel.Size#2eab455a","price":"1300.0","qnty":"5","id":"2","class":"class com.certus.dbmodel.ProductHasSize"}]
Thanks in advance.
BeanUtils.describe does not provide enough information. It will be fine if all types are primitive.
You will have to serialize each property independently. For fields that are not primitive types, serialize them. You also have to create the adapter for the actual type, so you can access its properties.
public class CustomTypeAdapter implements JsonSerializer<ProductHasSize> {
#Override
public JsonElement serialize(ProductHasSize phs, Type type, JsonSerializationContext jsc) {
JsonObject jsonObject = new JsonObject();
// try {
// Map<String, String> properties = BeanUtils.describe(object);
// //org.apache.commons.beanutils
// for (Map.Entry<String, String> entry : properties.entrySet()) {
// jsonObject.addProperty(entry.getKey(), entry.getValue());
// }
// } catch (Exception ex) {
// ex.printStackTrace();
// }
jsonObject.addProperty("price", phs.getPrice());
jsonObject.addProperty("quantity", phs.getQuantity());
JsonElement jsonProduct = jsc.serialize(phs.getProduct());
jsonObject.add("product", jsonProduct);
JsonElement jsonSize = jsc.serialize(phs.getSize());
jsonObject.add("size", jsonSize);
return jsonObject;
}
}
This page has a nice introduction: http://www.javacreed.com/gson-serialiser-example/
I have generated JSON in the following format
[{"empNo":"2390","empName":"JAMES","projects":{"projectId":209,"projectName":"Z560"}}]
How do I configure ObjectMapper for the above?
I have declared ObjectMapper as
private static final ObjectMapper om = new ObjectMapper();
static {
om.configure(JsonGenerator.Feature.QUOTE_FIELD_NAMES, false);
om.configure(JsonParser.Feature.ALLOW_UNQUOTED_FIELD_NAMES, true);
om.configure(SerializationConfig.Feature.WRITE_DATES_AS_TIMESTAMPS,
true);
om.configure(DeserializationConfig.Feature.FAIL_ON_UNKNOWN_PROPERTIES,
false);
om.getSerializationConfig().setSerializationInclusion
(JsonSerialize.Inclusion.NON_NULL);
}
However I am still getting the following error
com.sun.jersey.spi.container.ContainerResponse mapMappableContainerException
SEVERE: The exception contained within MappableContainerException could not
be mapped to a response, re-throwing to the HTTP container
org.codehaus.jackson.JsonParseException: Unexpected character ('b' (code 98)):
expected a valid value (number, String, array, object,
'true', 'false' or 'null') at [Source: java.io.StringReader#1fef0b44; line: 1,
column: 2]
Expected output is
{"empNo":"2390","empName":"JAMES","projectId":"209","projectName":"Z560"}
A bit lengthy, can be optimized. refer this for more.
public static void main(String[] args) throws IOException {
String originalJson = "{\"empNo\":\"2390\",\"empName\":\"JAMES\",\"projects\":{\"projectId\":209,\"projectName\":\"Z560\"}}";
try {
JSONObject jsonObject = new JSONObject(originalJson);
Map<String, Object> map = getMap(jsonObject);
System.out.println("My Old Map => " + map);
Map<String, Object> newMap = new HashMap<String, Object>();
for (Map.Entry<String, Object> entry : map.entrySet()) {
if (entry.getKey().equals("projects")) {
Map<String, Object> projectMap = (Map<String, Object>) entry.getValue();
for (Map.Entry<String, Object> entry1 : projectMap.entrySet()) {
newMap.put(entry1.getKey(), entry1.getValue());
}
} else {
newMap.put(entry.getKey(), entry.getValue().toString());
}
}
JSONObject jsonObject1 = new JSONObject(newMap);
System.out.println("My New Map => " + newMap);
System.out.println("Expected Json String => " + jsonObject1.toString());
} catch (JSONException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
private static Map getMap(JSONObject object) {
Map<String, Object> map = new HashMap<String, Object>();
Object jsonObject = null;
String key = null;
Object value = null;
try {
Iterator<String> keys = object.keys();
while (keys.hasNext()) {
key = null;
value = null;
key = keys.next();
if (null != key && !object.isNull(key)) {
value = object.get(key);
}
if (value instanceof JSONObject) {
map.put(key, getMap((JSONObject) value));
continue;
}
if (value instanceof JSONArray) {
JSONArray array = ((JSONArray) value);
List list = new ArrayList();
for (int i = 0 ; i < array.length() ; i++) {
jsonObject = array.get(i);
if (jsonObject instanceof JSONObject) {
list.add(getMap((JSONObject) jsonObject));
} else {
list.add(jsonObject);
}
}
map.put(key, list);
continue;
}
map.put(key, value);
}
} catch (Exception e) {
System.out.println(e);
}
return map;
}
Output
My Old Map => {projects={projectId=209, projectName=Z560},
empName=JAMES, empNo=2390}
My New Map => {empName=JAMES, empNo=2390, projectId=209,
projectName=Z560}
Expected Json String =>
{"empName":"JAMES","empNo":"2390","projectId":209,"projectName":"Z560"}
I have a json ouput like this -
{"menu": {
"id": "12",
"value": "File",
"popup": {
"menuitem": [
{"op1": "New", "op11": "CreateNewDoc()"},
{"op2": "Open", "op21": "OpenDoc()"},
{"op3": "Close", "op31": "CloseDoc()"}
]
}
}}
I want whatever is the key and order, it should return the key-value pair in a map like this and I do not want to harcode any key-
id=12
value=File
op1=New
op11=CreateNewDoc()
op2=Open
op21=OpenDoc()
op3=Close
op31=CloseDoc()
how will I do it?
Using the standard Java JSON Stream APIs, this will produce the java.util.Map you want:
Map<String, String> values = new HashMap<>();
String keyName = null;
JsonParser jsonParser = Json.createParser(new StringReader(json));
while (jsonParser.hasNext())
{
JsonParser.Event event = jsonParser.next();
if (JsonParser.Event.KEY_NAME.equals(event))
{
keyName = jsonParser.getString();
}
else if (JsonParser.Event.VALUE_STRING.equals(event))
{
values.put(keyName, jsonParser.getString());
}
}
I have used Jackson Json.
first. I changed Json string to JsonNode.
ex) JsonString to JsonNode
public static JsonNode jsonStringToJsonNode(String json){
ObjectMapper mp = new ObjectMapper();
try {
return mp.readValue(json, JsonNode.class);
} catch (JsonParseException e) {
} catch (JsonMappingException e) {
} catch (IOException e) {
}
return null;
}
Usage.
ex)
JsonNode json = jsonStringToJsonNode(jsonstring)
json.get("menu").get("id") => result 12
I hope it will help you.
Simply convert JSON string into Map<String,Object> then extract the desired values using Recursion.
Recursion method:
public static void process(String key, Object value, Map<String, String> newMap){
if (value instanceof String) {
newMap.put(key, (String) value);
} else if (value instanceof Map) {
Map<String, Object> map = (Map<String, Object>) value;
for (Entry<String, Object> entry : map.entrySet()) {
process(entry.getKey(), entry.getValue(), newMap);
}
} else if (value instanceof List) {
List<Object> list = (List<Object>) value;
for (Object obj : list) {
process(key, obj, newMap);
}
}
}
You can try any one.
sample code: (using Jackson Library)
TypeReference<Map<String, Object>> typeRef = new TypeReference<Map<String, Object>>() {};
ObjectMapper mapper = new ObjectMapper();
try {
Map<String, Object> data = mapper.readValue(jsonString, typeRef);
Map<String, String> newMap = new HashMap<String, String>();
process("menu", data.get("menu"), newMap);
System.out.println(new JSONObject(newMap));
} catch (Exception e) {
System.out.println("There might be some issue with the JSON string");
}
sample code: using GSON Library
Type type = new TypeToken<Map<String, Object>>() {}.getType();
Map<String, Object> data = new Gson().fromJson(jsonString, type);
Map<String, String> newMap = new HashMap<String, String>();
process("menu", data.get("menu"), newMap);
System.out.println(new GsonBuilder().setPrettyPrinting().create().toJson(newMap));
output:
{
"op1": "New",
"id": "12",
"op21": "OpenDoc()",
"op2": "Open",
"op3": "Close",
"op11": "CreateNewDoc()",
"value": "File",
"op31": "CloseDoc()"
}
I created a method "Json to HashTable" and vice versa. I use HashTable because "Java" there are no associative arrays. My problem now is when there is an array in the json. This means from "Java" an array of HashTable :/ does not work at all but I think the solution is to use "List >" ...
I see this somewhat complicated. Any help? Is that hard or I complicate too?
Json example:
{"Config":[{"Name":"method1","Uses":"0","Event":["Start","Play"],"Action":{"Class":"Ads","Options":{"Class":"Webview","Url":"http:\/\/test.com\/action.php","Time":"10"}}},{"Name":"method2","Uses":"12","Event":["Loading"],"MaxTimes":"5","Options":{"Class":"Ads"}}]}
View in: http://json.parser.online.fr/
My code:
public Hashtable<?, ?> JSonDecode(String data) {
Hashtable<String, Object> htJS = new Hashtable<String, Object>();
try {
JSONObject objJS = new JSONObject(data);
Iterator<String> it = objJS.keys();
String key = null;
Object value = null;
while (it.hasNext()) {
key = it.next();
value = objJS.get(key);
if (value instanceof JSONObject) {
value = JSonObjectToHashtable(value.toString());
}
if (value instanceof JSONArray) {
value = JSonArrayToHashtable(value.toString());
}
htJS.put((String) key, value);
}
} catch (JSONException e) {
e.printStackTrace();
// No valid json
return null;
}
return htJS;
}
public Hashtable<?, ?> JSonObjectToHashtable(String data) {
Hashtable<String, Object> htJS = new Hashtable<String, Object>();
JSONObject objJS;
try {
objJS = new JSONObject(data);
Iterator<String> it = objJS.keys();
String key = null;
Object value = null;
while (it.hasNext()) {
key = it.next();
value = objJS.get(key);
if (value instanceof JSONObject) {
value = JSonObjectToHashtable(value.toString());
}
htJS.put((String) key, value);
}
} catch (JSONException e) {
e.printStackTrace();
}
return htJS;
}
public List<Map<String, Object>> JSonArrayToHashtable(String data) {
List<Map<String, Object>> listMap = new ArrayList<Map<String,Object>>();
Map<String,Object> entry = new HashMap<String,Object>();
JSONArray objJSA;
try {
objJSA = new JSONArray(data);
for (int i = 0; i < objJSA.length(); i++) {
JSONObject objJS = objJSA.getJSONObject(i);
Iterator<String> it = objJS.keys();
String key = null;
Object value = null;
while (it.hasNext()) {
key = it.next();
value = objJS.get(key);
if (value instanceof JSONObject) {
value = JSonObjectToHashtable(value.toString());
}
entry.put((String) key, value);
}
listMap.add(entry);
entry = new HashMap<String,Object>();
}
} catch (JSONException e) {
e.printStackTrace();
}
return listMap;
}
Map (Hashtable) API is similar to JSONObject API. There is really no need to convert JSONObject to Map unless your application uses Maps consistently.
If you need to convert JSONObject to Map, the Map can be of type Map<String, Object>, where Object can be one of the following types:
String
Primitive (Integer, Float, etc)
Map
Collection (Array, List, etc) of the tree types mentioned above