JsonObject is converting String to JsonArray - java

Why is this changing the test element to a JSONArray and how do I stop it?
import net.sf.json.JSONArray
import net.sf.json.JSONObject
HashMap<String,Object> stuff = new HashMap<String,Object>()
stuff.put("name","alex")
stuff.put("age","21")
stuff.put("consent",true)
stuff.put("test",'[1,2,true]')
JsonBuilder a = new JsonBuilder(stuff)
JSONObject b = a.getContent()
However, when I look at b the test property is a JsonArray. How can I force it to keep it a String? Thanks!

Google tells me this is a pretty common problem with Groovy's JsonBuilder. If you can't change your data structures I'd suggest using gson instead, it gives you much better control over how your data is serialized and deserialized.
If you can change your data structures, I would suggest bundling all that stuff you're sticking in the Map in to a domain object. That way JsonBuilder will have data types to give it hints. I'm not 100% sure JsonBuilder will do the right thing, but it's more likely that it will if it doesn't have to guess at the data types.
If you had something like this simplified example
class Person {
String name;
int age;
boolean consent;
String test;
public Person(String name, int age, boolean consent, String test) {
...
}
}
Person person = new Person("alex", 21, true, "[1,2,true]");
JsonBuilder a = new JsonBuilder(person);
JSONObject b = a.getContent();
you might have better luck.

Related

How to properly serialize and deserialize an array of objects into/from json?

I'm trying to implement a friends list which needs to be stored in a .json file, in Kotlin/Java with libgdx, but this isn't neccesary(Java is fine).
My code for (1) doesn't work so instead of pasteing it here I'll just try to explain my design and only paste the one for (2) as this I believe is closer to a good implementation.
I made a "Friend" class. When adding a new friend the main thread created such an object, then I read the existing "FriendsList.json" into a string, edited the string by removing "]" and appending the serialized Friend object and a "]" to close the array.
I had and still have a feeling this isn't good, so I changed it.
I made a "FriendArray" class, in which I thought of storing "Friend" objects in an List. I think this would allow me to get rid of the string manipulation code, and just serialize the FriendList class itself, which would hopefully also be easier to read. One of the problems is that addFriendToListOfFriends() doesn't add the data in the objects (it adds "{}" instead of also inserting the name and id).
What do you think of (2) ? Do you know a better way of doing this?
(Just to be clear, I'm more interested in the design and less about compilable code)
import com.badlogic.gdx.files.FileHandle
import com.unciv.json.json (this is com.badlogic.gdx.utils.Json)
import java.io.Serializable
class FriendList() {
private val friendsListFileName = "FriendsList.json"
private val friendsListFileHandle = FileHandle(friendsListFileName)
private var friendListString = ""
var arrayOfFriends = FriendArray()
fun getFriendsListAsString(): String {
return friendsListFileHandle.readString()
}
fun addNewFriend(friendName: String, playerID: String) {
val friend = Friend(friendName, playerID)
arrayOfFriends.addFriendToListOfFriends(friendName, playerID)
saveFriendsList()
}
fun saveFriendsList(){
friendListString = getFriendsListAsString()
friendListString = friendListString.plus(json().prettyPrint(arrayOfFriends))
friendsListFileHandle.writeString(friendListString, false)
}
}
class Friend(val name: String, val userId: String)
class FriendArray(): Serializable {
var nrOfFriends = 0
var listOfFriends = listOf<Friend>()
fun addFriendToListOfFriends(friendName: String, playerID: String) {
var friend = Friend(friendName, playerID)
listOfFriends.plus(friend)
}
}
You don't realy need a class FriendArray for this. You can just searialize a list to JSON. Also it's easier to load the existing friend list to a list, add the new friend to the list and serialize the new list, instead of appending a string.
This way you won't have to worry about the correct JSON format or string manipulation. You just add an object to a list, and serialize the list.
Something like this should work (in java, sorry I don't know enough kotlin to implement this):
public void addFriendAndSerializeToFile(Friend friend) {
// load existing friend list from the file
Json json = new Json();
// here the first parameter is the List (or Collection) type and the second parameter is the type of the objects that are stored in the list
List<Friend> friendList = json.fromJson(List.class, Friend.class, friendsListFileHandle);
// add the new friend to the deserialized list
friendList.add(friend);
// serialize the whole new list to the file
String serializedFriendListWithNewFriendAdded = json.prettyPrint(friendList);
// write to the file handle
fileHandle.writeString(serializedFriendListWithNewFriendAdded, false);
}

Easy way to get to values from JSONArray

I recently started working with JSON in Java. We have been setting and getting our values as follows from this JSONArray:
[{"productId":"1"},{"productName":"hammer"}]
JSONObject jo = ja.getJSONObject(0);
We could easily get the values by calling jo.getString("productId"); which would return the 1.
The problem is that sometimes we get different types of JSON objects. They look like this:
[{"name":"productId", "value":"1"},{"name":"productName", "value":"hammer"}]
Is there a way to easily eliminate those predicate name/value and just group the actual name and value together (as in the first example)?
The short answer is no.
The longer answer is that you're not working with JSON, you're working with someone's misunderstanding of JSON.
Both of your examples look a bit like JSON, but they're both bogus.
[] is an array.
{} is an object.
Your first string [{"productId":"1"},{"productName":"hammer"}]
is an array of two objects, where each object has one property.
It's confusing to put dissimilar objects into an array together, but that's going on in both of your examples.
The second example [{"name":"productId", "value":"1"},{"name":"productName", "value":"hammer"}] shows an array of two objects, but again, the objects are dissimilar.
I think what they're going for is more like [{"productId":"1","productName":"hammer"}], so I guess the long answer to your question is that you need to go to whomever is providing this "JSON" and tell them to fix it.
To give you a clearer idea of the correspondence between objects (in Java and otherwise) and JSON, check out the Java program below:
public class Product {
String productName;
String productId;
public Product(String productId,String productName){
this.productName = productName;
this.productId = productId;
}
public String toString(){return toJSONString();}
public String toJSONString(){
return "{\"productId\":\""+productId+",\"productName:\""+productName+"\"}";
}
public static String arrayToJSONString(Product[] arry){
StringBuilder sb = new StringBuilder();
sb.append("["+arry[0]);
for (int n =1;n<arry.length;n++){
sb.append(","+arry[n]);
}
sb.append("]");
return sb.toString();
}
public static void main(String [] args){
Product p1 = new Product("1","hammer");
Product[] arry = {p1};
Product[] arry2 ={p1,new Product("2","shovel"), new Product("3","manure")};
System.out.println("One object");
System.out.println(" "+p1);
System.out.println("An array containing one object");
System.out.println(" "+Product.arrayToJSONString(arry));
System.out.println("An array containing three objects");
System.out.println(" "+Product.arrayToJSONString(arry2));
}
}
Here's the output showing the proper JSON representation:
One object
{"productId":"1,"productName:"hammer"}
An array containing one object
[{"productId":"1,"productName:"hammer"}]
An array containing three objects
[{"productId":"1,"productName:"hammer"},{"productId":"2,"productName:"shovel"},{"productId":"3,"productName:"manure"}]
(Newlines are an artifact of the HTML, not JSON)

GSON get string from complex json

I am trying to parse the JSON from this link: https://api.guildwars2.com/v2/items/56 , everything fine until i met the line: "infix_upgrade":{"attributes":[{"attribute":"Power","modifier":4},{"attribute":"Precision","modifier":3}]} ...
If i dont get this wrong: infix_upgradehas 1 element attributes inside him. attributes has 2 elements with 2 other inside them. Is this a 2 dimension array?
I have tried (code too long to post):
JsonObject _detailsObject = _rootObject.get("details").getAsJsonObject();
JsonObject infix_upgradeObject = _detailsObject.get("infix_upgrade").getAsJsonObject();
JsonElement _infix_upgrade_attributesElement = infix_upgradeObject.get("attributes");
JsonArray _infix_upgrade_attributesJsonArray = _infix_upgrade_attributesElement.getAsJsonArray();
The problem is that I dont know what to do next, also tried to continue transforming JsonArray into string array like this:
Type _listType = new TypeToken<List<String>>() {}.getType();
List<String> _details_infusion_slotsStringArray = new Gson().fromJson(_infix_upgrade_attributesJsonArray, _listType);
but im getting java.lang.IllegalStateException: Expected STRING but was BEGIN_OBJECT which i guess comes from the attributes...
With a proper formatting (JSONLint, for example, checks if the JSON data is valid and does the formatting, which makes the structure more clear than what the GW link gives), attributes looks actually like this:
"attributes": [
{
"attribute": "Power",
"modifier": 4
},
{
"attribute": "Precision",
"modifier": 3
}
]
So it's an array of JsonObject and each object as two key-value pairs. This is why the parser throws an error because you require that this array contains only String which is not the case.
So the actual type is:
Type _listType = new TypeToken<List<JsonObject>>(){}.getType();
The problem is that I dont know what to do next
Hold on. You are using Gson and Java is an OO language so I suggest you to create classes.
This would be easier for you to fetch the datas afterward and for the parsing since you just need to provide the class of the actual class the JSON data represents to the parser (some edge-cases could be handled by writing a custom serializer/deserializer).
The data is also better typed than this bunch of JsonObject/JsonArray/etc.
This will give you a good starting point:
class Equipment {
private String name;
private String description;
...
#SerializedName("game_types")
private List<String> gameTypes;
...
private Details details;
...
}
class Details {
...
#SerializedName("infix_upgrade")
private InfixUpgrade infixUpgrade;
...
}
class InfixUpgrade {
private List<Attribute> attributes;
...
}
class Attribute {
private String attribute;
private int modifier;
...
}
and then just give the type to the parser:
Equipment equipment = new Gson().fromJson(jsonString, Equipment.class);
Hope it helps! :)

Serialization of non-primitives using Jackson

I am trying to serialize a JSON object using Jackson and save into a mysql database using hibernate. All fields of my POJO class are able to be serialized except for any field that isn't a primitive.
public class Teacher {
private Set<Student> students;
private int id;
// getters and setters
}
In this case it would fail on students, creating an infinite recursive loop through the reference chain. I can stop it with #JsonIgnoreProperty but I want this field to be serialized. I am serializing my object like so:
ObjectMapper mapper = new ObjectMapper();
Teacher myTeacher = new Teacher();
mapper.writeValueAsString(teacher);
The only workaround I can think of is appending a string to the end of teacher while still ignoring the property but I am not sure if I will be able to read students as a JsonNode from the tree if I do this.
A way around this would be to use a pure Array or an ArrayList which are serialized fine with Jackson.
For example, I can serialize a class with all these parameters :
public class Map{
private short [][] someMapType;
private short [][] someOtherMap;
private ArrayList<Mill> someMills, otherMills;
private ArrayList<OtherPOJO> myPOJOList;
private String action = "myDefaultAction";
...
}
Where Mill and OtherPOJO are class with not much more than a couple of arrays and other primitives : pure POJOs.
It works fine both ways with Jackson and MongoJack (jackson serializer for MongoDb).
If you can't get away from the set than you have to understand properly what is the fundamental data structure in a set. This should help but you are probably already aware of that.
A way to work around this structure limitation would be to create non-dumb getters and setters. The main disadvantage behind this method is that you run one more for loop over all your elements every time you serialize or de-serialize. This might slightly reduce performance.
The getter is fairly simple :
public Student[] getStudents(){
return this.students.toArray();
}
And the setter is also pretty trivial :
public void setStudents(Student[] students){
this.students = new Set<Student>(); // Or anything that builds the right Set for you
for(int i = 0; i < students.length; i++){
this.students.add(students[i]);
}
}
I wrote it quickly, let me know if there is any bug.
I hope it helps!
Found a decent workaround:
#JsonIgnore
public Set<Student> getStudents() {
return students;
}
#JsonProperty("Students")
public String getStudentsForJson() {
String[] studentNames = new String[this.students.size()];
int i = 0;
for(Student student : this.students) {
studentNames[i] = student.getName();
i++;
}
return StringUtils.join(studentNames, ", ");
}
This saves all student names as one string which I'm able to easily serialize and deserialize as a field.

Convert Java List to Javascript Array

I have the following java code in my Android application and wanted a way to convert the Java list to an array that can be used in javascript:
Java:
public void onCompleted(List<GraphUser> users, Response response) {
for(int i = 0; i < users.size(); i++)
{
//add to an array object that can be used in Javascript
webView.loadUrl("javascript:fetchFriends(arrObj)");
}
}
Javascript:
//this is how I want to be able to use the object in Javascript
function parseFriends(usersObjectFromJava){
var users = [];
for (var i = 0; i < usersObjectFromJava.length; i++) {
var u = {
Id: usersObjectFromJava[i].id + "",
UserName: usersObjectFromJava[i].username,
FirstName: usersObjectFromJava[i].first_name,
LastName: usersObjectFromJava[i].last_name,
};
users[i] = u;
}
}
Could some help me with the Java code to create the usersObjectFromJava so that it can be used in javascript?
Use GSON
to convert java objects to JSON string, you can do it by
Gson gson = new Gson();
TestObject o1 = new TestObject("value1", 1);
TestObject o2 = new TestObject("value2", 2);
TestObject o3 = new TestObject("value3", 3);
List<TestObject> list = new ArrayList<TestObject>();
list.add(o1);
list.add(o2);
list.add(o3);
gson.toJson(list) will give you
[{"prop1":"value1","prop2":2},{"prop1":"value2","prop2":2},{"prop1":"value3","prop2":3}]
Now you can use JSON.parse(), to deserialize from JSON to Javascript Object.
I would assume doing this:
Java:
public void onCompleted(List<GraphUser> users, Response response) {
JSONArray arr = new JSONArray();
JSONObject tmp;
try {
for(int i = 0; i < users.size(); i++) {
tmp = new JSONObject();
tmp.put("Id",users.get(i).id); //some public getters inside GraphUser?
tmp.put("Username",users.get(i).username);
tmp.put("FirstName",users.get(i).first_name);
tmp.put("LastName",users.get(i).last_name);
arr.add(tmp);
}
webView.loadUrl("javascript:fetchFriends("+arr.toString()+")");
} catch(JSONException e){
//error handling
}
}
JavaScript:
function fetchFriends(usersObjectFromJava){
var users = usersObjectFromJava;
}
You will have to change the Java-Code a bit (i.e. using public getters or add more/less information to the JSONObjects.
JSON is included in Android by default, so no external libraries are necessary.
I hope i understood your problem.
Small thing i came across: you where using fetchFriends in Java but its called parseFriends in Javascript, I renamed them to fetchFriends
You can use Gson Library.
Gson gson = new GsonBuilder().create();
JsonArray jsonArray = gson.toJsonTree(your_list, TypeClass.class).getAsJsonArray();
http://google-gson.googlecode.com/svn/trunk/gson/docs/javadocs/com/google/gson/Gson.html
Use Jackson.
You'll need to add an " #JsonProperty" annotation to every property of your POJOs you want to pass, then do something like this:
String respStr = "";
for(Object whatever: MyList)
{
JSONObject dato = new JSONObject();
dato.put("FirstField", whatever.SomeData());
dato.put("SecondField", whatever.SomeData2());
StringEntity entity = new StringEntity(dato.toString());
post.setEntity(entity);
webView.loadUrl("javascript:fetchFriends("+entity+")");
}
I am not sure why no answer mentioned about jaxb. I am just thinking jaxb would be a good fit for this type of problems...
For a sample style of annotated jaxb class, please find this.
import java.util.ArrayList;
import java.util.List;
import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlRootElement;
#XmlRootElement
#XmlAccessorType(XmlAccessType.FIELD)
public class ResponseAsList {
private List < Object > list = new ArrayList < Object > ();
public ResponseAsList() {
// private default constructor for JAXB
}
public List < Object > getList() {
return list;
}
public void setList(List < Object > list) {
this.list = list;
}
}
You will stuff your data in these lists and you will marshal either in xml or a json. After you get a json to the client, you can do a var myArray = JSON.parse(response);...
Although I typically advocate using something like GSON or Jackson to do JSON conversions for you, its pretty easy to roll your own if you're in a limited environment (like Android) and don't want to bundle a bunch of dependencies.
public class JsonHelper {
public static String convertToJSON(List<GraphUser> users) {
StringBuilder sb = new StringBuilder();
for (GraphUser user : users) {
sb.append(convertToJSON(user));
}
return sb.toString();
}
public static String convertToJSON(GraphUser user) {
return new StringBuilder()
.append("{")
.append("\"id\":").append(user.getId()).append(",")
.append("\"admin\":").append(user.isAdmin() ? "true" : "false").append(",")
.append("\"name\":\"").append(user.getName()).append("\",")
.append("\"email\":\"").append(user.getEmail()).append("\"")
.append("}")
.toString();
}
}
You could obviously make a toJSON() method on GraphUser to put the logic if you prefer. Or use an injectable json helper library instead of static methods (I would). Or any number of other abstractions. Many developers prefer to separate representation of model objects into their own object, myself included. Personally, I might model it something like this if I wanted to avoid dependencies:
interface Marshaller<F,T> with methods T marshall(F obj) and F unmarshall(T obj)
interface JsonMarshaller<F> extends Marshaller<String>
class GraphUserMarshaller implements JsonMarshaller<GraphUser>
class GraphUserCollectionMarshaller implements JsonMarshaller<Collection<GraphUser>> which could do type-checking or use the visitor pattern or something to determine the best way to represent this type of collection of objects.
Along the way, I'm sure you'll find some repeated code to extract to super- or composite- classes, particularly once you start modeling collection marshallers this way. Although this can get to be pretty verbose (and tedious), it works particularly well in resource-constrained environments where you want to limit the number of libraries on which you depend.
You can use the Google Gson library (http://code.google.com/p/google-gson/) to convert the Java List Object to JSON. Ensure that the right fields are set like ID, UserName, FirstName, etc and on the java script side that same code would work.
Its just an example, First add javascript interface. It will be a bridge between javascript and java code.
webView.addJavascriptInterface(new JSInterface(), "interface");
In javascript you can add like this,
"window.interface.setPageCount(pageCount1);"
The interface is a keyword in common between java and javascript. create a class JSInterace and define a method setPageCount(int a). The script will return a value, and you can use that value in your java method

Categories

Resources