Gson Custom Serializer Not Called - java

I have a class that doesn't serialize properly with Gson (class is just name and HashMap) so I wrote a custom serializer to print the name and the key, value pair from the HashMap.
public JsonElement serialize(SpecificationGroupList sgl, Type typeofT,
JsonSerializationContext context) {
System.out.println("here");
JsonObject ret = new JsonObject();
ret.addProperty("GroupName", sgl.getGroupName());
JsonArray jsonArray = new JsonArray();
ret.add("SpecificationPartList", jsonArray);
for (Entry<String, String> entry : sgl.getSpecificationPairList().entrySet()) {
JsonObject temp = new JsonObject();
temp.addProperty(entry.getKey(), entry.getValue());
jsonArray.add(temp);
}
return ret;
}
To get it to print appropriately, I've registered the custom serializer but when I go to print the class, it doesn't actually use the serializer. I can tell because I have the serializer printing "here" and it never prints.
private void printProducts() {
Gson gson = new GsonBuilder().setPrettyPrinting()
.registerTypeAdapter(SpecificationGroupList.class, new SpecGroupListSerializer())
.create();
System.out.println(gson.getAdapter(SpecificationGroupList.class).toString());
for (Item i : items) {
System.out.println(gson.toJson(i));
System.out.println("sgl" + gson.toJson(i.getSpecificationGroupList()));
}
}
Also, this is what actually prints and serializing the entire object doesn't work as I would expect nor does trying just to print the object directly.
{
"ItemNumber": "22-148-842",
"NeweggItemNumber": "N82E16822148842",
"Title": "Seagate Savvio 15K.3 ST9300653SS 300GB 15000 RPM 2.5\" SAS 6Gb/s Internal Enterprise Hard Drive -Bare Drive",
"specificationGroupList": []
}
sgl[]
Any help would be greatly appreciated.

Of course, I can't directly copy the code in as I've gotten rid of it and I wasn't yet to the point where I'd start versioning but essentially:
ArrayList<Item> items = new ArrayList<Item>();
Gson gson = new Gson
for (Item i : items){
i.setId(something);
}
for (Item i : items){
//...
//send query and get response here
//...
i = gson.fromJson(in, Item.class);
}
Setting i to the returned Item didn't really work and so then when I tried to serialize, the HashMap in my objects wasn't set properly as #Perception noted. I "solved" it by creating a List of strings to hold something and then added the returned Items to the ArrayList instead of trying to assign it to the existing Item.

Related

Adding a new key and value to the end of a json array in java [duplicate]

I've a problem trying to make my page printing out the JSONObject in the order i want. In my code, I entered this:
JSONObject myObject = new JSONObject();
myObject.put("userid", "User 1");
myObject.put("amount", "24.23");
myObject.put("success", "NO");
However, when I see the display on my page, it gives:
JSON formatted string: [{"success":"NO", "userid":"User 1", "bid":24.23}]
I need it in the order of userid, amount, then success. Already tried re-ordering in the code, but to no avail. I've also tried .append....need some help here thanks!!
You cannot and should not rely on the ordering of elements within a JSON object.
From the JSON specification at https://www.json.org/
An object is an unordered set of
name/value pairs
As a consequence,
JSON libraries are free to rearrange the order of the elements as they see fit.
This is not a bug.
I agree with the other answers. You cannot rely on the ordering of JSON elements.
However if we need to have an ordered JSON, one solution might be to prepare a LinkedHashMap object with elements and convert it to JSONObject.
#Test
def void testOrdered() {
Map obj = new LinkedHashMap()
obj.put("a", "foo1")
obj.put("b", new Integer(100))
obj.put("c", new Double(1000.21))
obj.put("d", new Boolean(true))
obj.put("e", "foo2")
obj.put("f", "foo3")
obj.put("g", "foo4")
obj.put("h", "foo5")
obj.put("x", null)
JSONObject json = (JSONObject) obj
logger.info("Ordered Json : %s", json.toString())
String expectedJsonString = """{"a":"foo1","b":100,"c":1000.21,"d":true,"e":"foo2","f":"foo3","g":"foo4","h":"foo5"}"""
assertEquals(expectedJsonString, json.toString())
JSONAssert.assertEquals(JSONSerializer.toJSON(expectedJsonString), json)
}
Normally the order is not preserved as below.
#Test
def void testUnordered() {
Map obj = new HashMap()
obj.put("a", "foo1")
obj.put("b", new Integer(100))
obj.put("c", new Double(1000.21))
obj.put("d", new Boolean(true))
obj.put("e", "foo2")
obj.put("f", "foo3")
obj.put("g", "foo4")
obj.put("h", "foo5")
obj.put("x", null)
JSONObject json = (JSONObject) obj
logger.info("Unordered Json : %s", json.toString(3, 3))
String unexpectedJsonString = """{"a":"foo1","b":100,"c":1000.21,"d":true,"e":"foo2","f":"foo3","g":"foo4","h":"foo5"}"""
// string representation of json objects are different
assertFalse(unexpectedJsonString.equals(json.toString()))
// json objects are equal
JSONAssert.assertEquals(JSONSerializer.toJSON(unexpectedJsonString), json)
}
You may check my post too: http://www.flyingtomoon.com/2011/04/preserving-order-in-json.html
u can retain the order, if u use JsonObject that belongs to com.google.gson :D
JsonObject responseObj = new JsonObject();
responseObj.addProperty("userid", "User 1");
responseObj.addProperty("amount", "24.23");
responseObj.addProperty("success", "NO");
Usage of this JsonObject doesn't even bother using Map<>
CHEERS!!!
Real answer can be found in specification, json is unordered.
However as a human reader I ordered my elements in order of importance. Not only is it a more logic way, it happened to be easier to read. Maybe the author of the specification never had to read JSON, I do.. So, Here comes a fix:
/**
* I got really tired of JSON rearranging added properties.
* Specification states:
* "An object is an unordered set of name/value pairs"
* StackOverflow states:
* As a consequence, JSON libraries are free to rearrange the order of the elements as they see fit.
* I state:
* My implementation will freely arrange added properties, IN SEQUENCE ORDER!
* Why did I do it? Cause of readability of created JSON document!
*/
private static class OrderedJSONObjectFactory {
private static Logger log = Logger.getLogger(OrderedJSONObjectFactory.class.getName());
private static boolean setupDone = false;
private static Field JSONObjectMapField = null;
private static void setupFieldAccessor() {
if( !setupDone ) {
setupDone = true;
try {
JSONObjectMapField = JSONObject.class.getDeclaredField("map");
JSONObjectMapField.setAccessible(true);
} catch (NoSuchFieldException ignored) {
log.warning("JSONObject implementation has changed, returning unmodified instance");
}
}
}
private static JSONObject create() {
setupFieldAccessor();
JSONObject result = new JSONObject();
try {
if (JSONObjectMapField != null) {
JSONObjectMapField.set(result, new LinkedHashMap<>());
}
}catch (IllegalAccessException ignored) {}
return result;
}
}
from lemiorhan example
i can solve with just change some line of lemiorhan's code
use:
JSONObject json = new JSONObject(obj);
instead of this:
JSONObject json = (JSONObject) obj
so in my test code is :
Map item_sub2 = new LinkedHashMap();
item_sub2.put("name", "flare");
item_sub2.put("val1", "val1");
item_sub2.put("val2", "val2");
item_sub2.put("size",102);
JSONArray itemarray2 = new JSONArray();
itemarray2.add(item_sub2);
itemarray2.add(item_sub2);//just for test
itemarray2.add(item_sub2);//just for test
Map item_sub1 = new LinkedHashMap();
item_sub1.put("name", "flare");
item_sub1.put("val1", "val1");
item_sub1.put("val2", "val2");
item_sub1.put("children",itemarray2);
JSONArray itemarray = new JSONArray();
itemarray.add(item_sub1);
itemarray.add(item_sub1);//just for test
itemarray.add(item_sub1);//just for test
Map item_root = new LinkedHashMap();
item_root.put("name", "flare");
item_root.put("children",itemarray);
JSONObject json = new JSONObject(item_root);
System.out.println(json.toJSONString());
JavaScript objects, and JSON, have no way to set the order for the keys. You might get it right in Java (I don't know how Java objects work, really) but if it's going to a web client or another consumer of the JSON, there is no guarantee as to the order of keys.
Download "json simple 1.1 jar" from this https://code.google.com/p/json-simple/downloads/detail?name=json_simple-1.1.jar&can=2&q=
And add the jar file to your lib folder
using JSONValue you can convert LinkedHashMap to json string
For those who're using maven, please try com.github.tsohr/json
<!-- https://mvnrepository.com/artifact/com.github.tsohr/json -->
<dependency>
<groupId>com.github.tsohr</groupId>
<artifactId>json</artifactId>
<version>0.0.1</version>
</dependency>
It's forked from JSON-java but switch its map implementation with LinkedHashMap which #lemiorhan noted above.
As all are telling you, JSON does not maintain "sequence" but array does, maybe this could convince you:
Ordered JSONObject
For Java code, Create a POJO class for your object instead of a JSONObject.
and use JSONEncapsulator for your POJO class.
that way order of elements depends on the order of getter setters in your POJO class.
for eg. POJO class will be like
Class myObj{
String userID;
String amount;
String success;
// getter setters in any order that you want
and where you need to send your json object in response
JSONContentEncapsulator<myObj> JSONObject = new JSONEncapsulator<myObj>("myObject");
JSONObject.setObject(myObj);
return Response.status(Status.OK).entity(JSONObject).build();
The response of this line will be
{myObject : {//attributes order same as getter setter order.}}
The main intention here is to send an ordered JSON object as response. We don't need javax.json.JsonObject to achieve that. We could create the ordered json as a string.
First create a LinkedHashMap with all key value pairs in required order. Then generate the json in string as shown below.
Its much easier with Java 8.
public Response getJSONResponse() {
Map<String, String> linkedHashMap = new LinkedHashMap<>();
linkedHashMap.put("A", "1");
linkedHashMap.put("B", "2");
linkedHashMap.put("C", "3");
String jsonStr = linkedHashMap.entrySet().stream()
.map(x -> "\"" + x.getKey() + "\":\"" + x.getValue() + "\"")
.collect(Collectors.joining(",", "{", "}"));
return Response.ok(jsonStr).build();
}
The response return by this function would be following:
{"A":"1","B":"2","C":"3"}
Underscore-java uses linkedhashmap to store key/value for json. I am the maintainer of the project.
Map<String, Object> myObject = new LinkedHashMap<>();
myObject.put("userid", "User 1");
myObject.put("amount", "24.23");
myObject.put("success", "NO");
System.out.println(U.toJson(myObject));
I found a "neat" reflection tweak on "the interwebs" that I like to share.
(origin: https://towardsdatascience.com/create-an-ordered-jsonobject-in-java-fb9629247d76)
It is about to change underlying collection in org.json.JSONObject to an un-ordering one (LinkedHashMap) by reflection API.
I tested succesfully:
import java.lang.reflect.Field;
import java.util.LinkedHashMap;
import org.json.JSONObject;
private static void makeJSONObjLinear(JSONObject jsonObject) {
try {
Field changeMap = jsonObject.getClass().getDeclaredField("map");
changeMap.setAccessible(true);
changeMap.set(jsonObject, new LinkedHashMap<>());
changeMap.setAccessible(false);
} catch (IllegalAccessException | NoSuchFieldException e) {
e.printStackTrace();
}
}
[...]
JSONObject requestBody = new JSONObject();
makeJSONObjLinear(requestBody);
requestBody.put("username", login);
requestBody.put("password", password);
[...]
// returned '{"username": "billy_778", "password": "********"}' == unordered
// instead of '{"password": "********", "username": "billy_778"}' == ordered (by key)
Just add the order with this tag
#JsonPropertyOrder({ "property1", "property2"})
Not sure if I am late to the party but I found this nice example that overrides the JSONObject constructor and makes sure that the JSON data are output in the same way as they are added. Behind the scenes JSONObject uses the MAP and MAP does not guarantee the order hence we need to override it to make sure we are receiving our JSON as per our order.
If you add this to your JSONObject then the resulting JSON would be in the same order as you have created it.
import java.io.IOException;
import java.lang.reflect.Field;
import java.util.LinkedHashMap;
import org.json.JSONObject;
import lombok.extern.java.Log;
#Log
public class JSONOrder {
public static void main(String[] args) throws IOException {
JSONObject jsontest = new JSONObject();
try {
Field changeMap = jsonEvent.getClass().getDeclaredField("map");
changeMap.setAccessible(true);
changeMap.set(jsonEvent, new LinkedHashMap<>());
changeMap.setAccessible(false);
} catch (IllegalAccessException | NoSuchFieldException e) {
log.info(e.getMessage());
}
jsontest.put("one", "I should be first");
jsonEvent.put("two", "I should be second");
jsonEvent.put("third", "I should be third");
System.out.println(jsonEvent);
}
}
Just use LinkedHashMap to keep de order and transform it to json with jackson
import com.fasterxml.jackson.databind.ObjectMapper;
import java.util.LinkedHashMap;
LinkedHashMap<String, Object> obj = new LinkedHashMap<String, Object>();
stats.put("aaa", "aaa");
stats.put("bbb", "bbb");
stats.put("ccc", "ccc");
ObjectMapper mapper = new ObjectMapper();
String json = mapper.writerWithDefaultPrettyPrinter().writeValueAsString(obj);
System.out.println(json);
maven dependency
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.9.10.7</version>
</dependency>
I just want the order for android unit tests that are somehow randomly changing overtime with this cool org.json.JSONObject, even thou it looks like it uses linked map but probably depends on api you compile it with or something, so it has different impl. with different android api probably.
I would suggest something like this:
object Json {
#SuppressLint("DiscouragedPrivateApi")
fun Object() = org.json.JSONObject().apply {
runCatching {
val nameValuePairs: Field = javaClass.getDeclaredField("nameValuePairs")
nameValuePairs.isAccessible = true
nameValuePairs.set(this, LinkedHashMap<String, Any?>())
}.onFailure { it.printStackTrace() }
}
}
Usage:
val jsonObject = Json.Object()
...
This is just some possibility I use it little differently so I modified it to post here. Sure gson or other lib is another option.
Suggestions that specification is bla bla are so shortsighted here, why you guys even post it, who cares about 15 years old json spec, everyone wants it ordered anyway.

JSON parameter is like nothing I have seen

I'm working on some JSON converting to POJO and the server I'm getting response of is sending a JSON like this:
"Availability":{
"StatusCode":"A",
"BreakDown":{
"2017-10-27":"A"
}
}
How can I save this ( "2017-10-27":"A" )? It changes with each of my request so it should be dynamic! Is it even possible?
If you are going the value currently represented as "2017-10-27":"A", you have to know it is hold in the variable "BreakDown". So you need to query this variable with jsonPath: $.Availability.BreakDown.
it will give this JSON object:
{"2017-10-27":"A"}
Hope it answer your question
The first answer is pretty accurate but to extract the key and value without directly referencing is the target since they are dynamic.
var x = obj.Availability.Breakdown;
for(var key in x){
console.log(key);
console.log(x[key]);
}
This way you get the key and the value both and use it as you like.
Plus, if there are multiple key-value pair inside var x then they can also be reached with this loop.
Assuming that with "should be dynamic" you mean that the content that you want to save (the one inside BreakDown) could change (even the type) for each request and assuming that your example of the json is:
Test.json:
{
"StatusCode":"A",
"BreakDown":{
"2017-10-27":"A"
}
}
You could use the Gson library to get the info that you want. Because every class has Object as a superclass, you could deserialize your json as a Map<Object, Object>.
public static void main(String args[]) {
Gson gson = new Gson();
Map<Object, Object> breakDown=null;
String filename="/.../Test.json";
JsonReader reader = null;
try {
reader = new JsonReader(new FileReader(filename));
} catch (FileNotFoundException e) {
e.printStackTrace();
}
final Type type = new TypeToken<Map<Object,Object>>() {}.getType();
Map <Object,Object> conv= gson.fromJson(reader, type);
for (Map.Entry<Object, Object> entry : conv.entrySet())
{
System.out.println(entry.getKey() + "/" + entry.getValue());
if(entry.getKey().equals("BreakDown"))
{
breakDown= (Map<Object, Object>) entry.getValue();
}
}
if(breakDown!=null){
Map.Entry first= breakDown.entrySet().iterator().next();
System.out.println(first.getKey());
System.out.println(first.getValue());
}
}
The Map<Object, Object> breakDown map is also of Objects because I'm assuming that the key and the value could be different of the example that you posted. Otherwise, if the key is always a date and the value a string, can be defined as Map<Date, String> breakDown.

How to modify the JSON data and return the updated JSON data

We have a requirement to update the JSON data in middle and need to return the updated JSON data using java. Also it should support any type of JSON data.
ex:
Assume {object:{"color":"red","shape":"Triangle"}} is the JSON data and in this we need to update the shape value to Rectangle and we need to return the updated JSON data as below:
{object:{"color":"red","shape":"Rectangle"}}
For this we need to pass the element path ( which element we need to update) and updateText and JSON Data to the JAVA code.
here is the methodCall:
updateValue("object/shape", "Rectangle", "{object:{"color":"red","shape":"Triangle"}}")
We tried below code using Gson library. But with this code we are able to update the targeted Json element, but the requirement is to return the entire JSON data with the updated value.
So please suggest how do we re-build the JSON data with the updated text.
Below is the code we tried to update the Json Data.
public String updateValue(String keyPath, String updateText, String jsonText) {
String[] keys = keyPath.split("/");
JsonParser jsonParser = new JsonParser();
JsonObject jsonObject = (JsonObject) jsonParser.parse(jsonText);
String result = "";
for(String key : keys)
{
if (jsonObject.get(key) instanceof JsonObject)
{
jsonObject = (JsonObject)jsonObject.get(key);
}
else if(jsonObject.get(key) instanceof JsonArray)
{
JsonArray jsonArray = (JsonArray)jsonObject.get(key);
result = jsonArray.toString();
}
else
{
result = jsonObject.get(key).toString();
}
}
result = result.replace(result, updateText);
return result;
}
The problem lies in the way you do the replacements. When you translate the JsonObject to String, you lose the object, and after replacement, you just have the replaced String. To fix it, you need to operate directly on the object, instead of the String counterpart. Because JsonObject is mutable, holding a reference to the input will reflect the changes. One drawback is you can't replace a value in a JsonArray this way, partly because you don't know which element to replace. To accomplish that, you will need a little more in the input(either the value to replace or the element position).
public String updateValue(String keyPath, String updateText, String jsonText) {
String[] keys = keyPath.split("/");
JsonParser jsonParser = new JsonParser();
JsonObject jsonObject = (JsonObject) jsonParser.parse(jsonText);
JsonObject returnVal = jsonObject; // This holds the ref to target json object
JsonPrimitive jp = new JsonPrimitive(updateText);
String finalKey = keys[keys.length - 1];
for(String key : keys)
{
if (jsonObject.get(key).isJsonObject())
{
jsonObject = (JsonObject)jsonObject.get(key);
}
}
jsonObject.remove(finalKey);
jsonObject.add(finalKey, jp);
return returnVal.toString();
}
You can use JsonPath lib for that and try using the following code.
private static final Configuration configuration = Configuration.builder()
.jsonProvider(new JacksonJsonNodeJsonProvider())
.mappingProvider(new JacksonMappingProvider())
.build();
JsonNode updatedJson = JsonPath.using(configuration).parse(originaljson)
.set("use the path to go for value", "new value").json();
json = updatedJson.toString();

Get JSON key name using GSON

I have a JSON array which contains objects such as this:
{
"bjones": {
"fname": "Betty",
"lname": "Jones",
"password": "ababab",
"level": "manager"
}
}
my User class has a username which would require the JSON object's key to be used. How would I get the key of my JSON object?
What I have now is getting everything and creating a new User object, but leaving the username null. Which is understandable because my JSON object does not contain a key/value pair for "username":"value".
Gson gson = new Gson();
JsonParser p = new JsonParser();
JsonReader file = new JsonReader(new FileReader(this.filename));
JsonObject result = p.parse(file).getAsJsonObject().getAsJsonObject("bjones");
User newUser = gson.fromJson(result, User.class);
// newUser.username = null
// newUser.fname = "Betty"
// newUser.lname = "Jones"
// newUser.password = "ababab"
// newUser.level = "manager"
edit:
I'm trying to insert "bjones" into newUser.username with Gson, sorry for the lack of clarification
Use entrySet to get the keys. Loop through the entries and create a User for every key.
JsonObject result = p.parse(file).getAsJsonObject();
Set<Map.Entry<String, JsonElement>> entrySet = result.entrySet();
for(Map.Entry<String, JsonElement> entry : entrySet) {
User newUser = gson.fromJson(p.getAsJsonObject(entry.getKey()), User.class);
newUser.username = entry.getKey();
//code...
}
Using keySet() directly excludes the necessity in iteration:
ArrayList<String> objectKeys =
new ArrayList<String>(
myJsonObject.keySet());
Your JSON is fairly simple, so even the manual sort of methods (like creating maps of strings etc for type) will work fine.
For complex JSONs(where there are many nested complex objects and lists of other complex objects inside your JSON), you can create POJO for your JSON with some tool like http://www.jsonschema2pojo.org/
And then just :
final Gson gson = new Gson();
final MyJsonModel obj = gson.fromJson(response, MyJsonModel.class);
// Just access your stuff in object. Example
System.out.println(obj.getResponse().getResults().get(0).getId());

how do I find out a JSON Object return JSON Array or string in android

I have a json like the following. how do I find out a JSON Object return JSON Array or string in android.
{
"green_spots": [
......
],
"yellow_spots": "No yellow spot available",
"red_spots": "No red spot available"
}
The JSON objects retrurn Array when values is present else return a String like "No green/red/yellow spot available". I done the with following way. but is there any other way to do? because alert string is changed the If will not work.
JSONObject obj = new JSONObject(response);
String green = obj.getString("green_spots");
// Green spots
if ("No green spot available".equalsIgnoreCase(green)) {
Log.v("search by hour", "No green spot available");
} else {
JSONArray greenArray = obj.getJSONArray("green_spots");
....
}
Object object = jsonObject.get("key");
if (object instanceof JSONObject) {
// It is json object
} else if (object instanceof JSONArray) {
// It is Json Array
} else {
// It is a String
}
You can use instanceof
instead of getString do just obj.get which will return an Object, check if the object is instanceof String or JSONArray
EDIT:
here is a bit of sample code to go with this:
Object itineraries = planObject.get("itineraries");
if (itineraries instanceof JSONObject) {
JSONObject itinerary = (JSONObject) itineraries;
// right now, itinerary is your single item
}
else {
JSONArray array = (JSONArray) itineraries;
// do whatever you want with the array of itineraries
}
JSONObject obj = new JSONObject(response);
JSONArray greenArray = obj.getJSONArray("green_spots");
if(greenArray!=null){
do your work with greenArray here
}else{
Log.v("search by hour", "No green spot available");
}
Simple just print the object like Log.e("TAG","See>>"JsonObject.toString);
if response is in {} block then it is object if it is in [] its array
Warning: This information may be superfluous, but it might prove to be an alternative approach to this problem.
You can use Jackson Object Mapper to convert a JSON file to a HashMap.
public static HashMap<String, Object> jsonToHashMap(
String jsonString) {
Map<String, Object> map = new HashMap<String, Object>();
ObjectMapper mapper = new ObjectMapper();
try {
// convert JSON string to Map
map = mapper.readValue(jsonString,
new TypeReference<HashMap<String, Object>>() {
});
} catch (Exception e) {
e.printStackTrace();
}
return (HashMap<String, Object>) map;
}
This automatically creates a HashMap of appropriate objects. You can then use instanceof or figure out another way to use those objects as appropriate/required.

Categories