Parsing complex nested JSON data with Gson - java

I'm using Gson to parse a JSON string. I want to convert this to an object using a container class and embedded static classes. To some extent this has been possible, but I want to treat the content of stuff1 and stuff2 as arrays, for example, stuff1 is an array containing other_stuff1 and other_stuff2. This is so I can reference the object in a fashion like these: object.integer, object.stuff1.get("other_stuff1").name, or object.stuff2.get("other_stuff3").more. (for the last one, I could be interested in looping over more to get each item.
For example, in PHP, I would use this:
<?php
echo "<pre>";
$object = json_decode(file_get_contents("THE JSON FILENAME"));
foreach($object->stuff1 as $name=>$data) {
echo $name . ":\n"; // other_stuff1 or other_stuff2
echo $unlockable->description . "\n\n"; // Got lots of stuff or Got even more stuff.
}
?>
I want to be able to reference in a similar way, loading the JSON to an object to be used on the fly.
It is crucial that, while some degree of change can be made to the JSON, that the names of the elements remain and be referable and retrievable.
I've included JSON, very similar to the one I'm using, below.
{
"integer":"12345",
"stuff1":{
"other_stuff1":{
"name":"a_name",
"description":"Got lots of stuff.",
"boolean":false
},
"other_stuff2":{
"name":"another_name",
"description":"Got even more stuff",
"boolean":true
}
},
"stuff2":{
"other_stuff3":{
"name":"a_name",
"description":"Got even more stuff",
"boolean":false,
"more":{
"option1":{
"name":"hello"
},
"option2":{
"name":"goodbye"
}
}
},
}
}
I've gone through a number of reference guides and tutorials, and I can't find a way to interpret this the way I'm trying to.
I'd really appreciate it if someone could give me a pointer. I can't find any tutorials that take into account that a) I want multiple objects in an array-style list, referable by the IDs (like with other_stuff1 and other_stuff2), and b) I want to also be able to loop over the items without providing the IDs.

You should define a Java class with fields named after the keys you need. You can use Maps (not arrays) to get the .get("key") behavior you describe. For example:
class Container {
private final int integer;
private final HashMap<String, Stuff> stuff1;
private final HashMap<String, Stuff> stuff2;
}
class Stuff {
private final String name;
private final String description;
#SerializedName("boolean") private final boolean bool;
private final HashMap<String, Option> more;
}
class Option {
private final String name;
}
For the "boolean" field you need need to use a different variable name since boolean is a reserved keyword.
You can then do:
Container c = gson.fromJson(jsonString, Container.class);
for(Stuff s : c.getStuff1().values()) {
System.out.println(s.getName());
}

Related

Return dynamic or union type from lambda in Java

I'm returning to Java after having been away for a very, very long time, and I'm trying to do in Java the sort of thing that I've often done in Javascript or Typescript. But I can't figure it out.
I'm trying to create a mapping between two different systems to easily map types and copy values from one system to the other. The systems are Neo4j and Excel, although my question doesn't really have anything to do with either of those. Basically the idea is that users can export data from Neo4j to an Excel sheet, edit values there, and then import it again. (I know! It's a terrible idea! But they really want it and we can blame them if they mess up.)
Still, at the very least I'd like them to not change the types of data in neo4j properties, so I'm building a fairly strict mapping:
public enum Neo4jExcelDataTypeMapping {
STRING("String", cell -> cell.getStringCellValue()),
INTEGER("Integer", cell -> cell.getNumericCellValue()),
FLOAT("Float", cell -> cell.getNumericCellValue()),
BOOLEAN("Boolean", cell -> cell.getBooleanCellValue()),
STRING_ARRAY("String[]",
cell -> Neo4jExcelDataTypeMapping.deserializeArrayToString(cell.getStringCellValue()),
value -> serializeArray((Object[]) value)
),
DATE("Date",
cell -> LocalDate.parse(cell.getStringCellValue()),
value -> ((LocalDate)value).format(DateTimeFormatter.ISO_LOCAL_DATE)),
);
interface ExcelCellReader {
Object readValue(Cell cell);
}
interface ExcelCellWriter {
Object writeValue(Object value);
}
public final String className;
public final ExcelCellReader excelCellReader;
public final ExcelCellWriter excelCellWriter;
private Neo4jExcelDataTypeMapping(String className, ExcelCellReader excelCellReader) {
this.className = className;
this.excelCellReader = excelCellReader;
this.excelCellWriter = value -> value;
}
private Neo4jExcelDataTypeMapping(String className, ExcelCellReader excelCellReader, ExcelCellWriter excelCellWriter) {
this.className = className;
this.excelCellReader = excelCellReader;
this.excelCellWriter = excelCellWriter;
}
public static Neo4jExcelDataTypeMapping fromNeo4jType(String type) {
for (Neo4jExcelDataTypeMapping instance : Neo4jExcelDataTypeMapping.values()) {
if (instance.className.equals(type)) {
return instance;
}
}
return null;
}
}
(I'm leaving out the serialization/deserialization of arrays in a readable manner, because that's a whole separate issue.)
This code seems fine. The IDE doesn't complain about it at least. But then I try to use it in a unit test:
#Test
public void testStringMapping() {
String testString = "Test String";
VirtualNode node = new VirtualNode(1);
node.setProperty("test", testString);
Cell cell = row.createCell(0);
String neo4jType = node.getProperty("test").getClass().getSimpleName();
Neo4jExcelDataTypeMapping mapping = Neo4jExcelDataTypeMapping.fromNeo4jType(neo4jType);
cell.setCellValue(mapping.excelCellWriter.writeValue(node.getProperty("test")));
}
Here, the problem is that cell.setCellValue() expects a specific type. It's heavily overloaded, so it can accept String, Integer, Float, various Dates, and more. But not Object. Now all the objects I'm returning from my lambda will be one of those types, but the lambda itself says it's returning an Object, and Java doesn't like that.
In TypeScript, I might have it return String|Integer|Float|Date, but you can't do that in Java. Is it possible to do this with generics somehow?
I don't want to explicitly cast in cell.setCellValue((String)mapping.excelCellWriter.writeValue(node.getProperty("test"))); because this is just going to be a single line that should work for all data types. I wrote the enum exactly to handle this, but it can't seem to handle this one final cast. I suspect some generic type magic could do it, but I never really mastered that aspect of Java.
(Also: I'm not a fan of the excelWriter.writeValue(). Is there a way to make excelWriter() both a property of the enum and the function that's called? Or is that too JavaScript of me?)

How extract the first element of a JsonArray that is an Integer and the other are JsonElements?

I'm on a Java Spring Boot project that makes API requests using RestTemplates.
Trying to implement pagination, makes the new JsonArray incoming has as first element an Integer and the rest are JsonElements.
Without pagination the value of the json incoming is:
[
{
"id":1234,
"name": null,
"...":...
},...
]
And these objects with their getter and setters correctly implented. p.d I don't need all the attributes of the incoming jsonelement.
public class WorkListBean extends WorkBean{
private List<WorkBean> lsWorkBean;
}
public class WorkBean implements Serializable {
private static final long serialVersionUID = -...L;
private long id;
private String name;
}
This returns WorkListBean[] to the client and works pretty well.
But with pagination implemented the incoming json is like this:
[
1,
{
"id":1234,
"name": null
},...
]
How could I extract this first Integer of the array?
I think maybe I could use a Deserializer with a Object Mapper to make a custom object that owns a Integer and a list or array of WorkBean. But I'm a little bit lost with this terms. Can anyone confirm am pointing in a good view?
Thanks in advance,
Grettings LaGallinaturuleta
That's a badly designed schema. Allthough it's technically allowed, putting elements of different types in the same array will make every client suffer.
If you can change the server side you're talking to, they should use a more user-friendly schema, like this:
{
"pageNumber":0,
"size":5,
"totalPages":4,
"content":[
{"studentId":"1","name":"Bryan","gender":"Male","age":20},
{"studentId":"2","name":"Ben","gender":"Male","age":22},
{"studentId":"3","name":"Lisa","gender":"Female","age":24},
{"studentId":"4","name":"Sarah","gender":"Female","age":26},
{"studentId":"5","name":"Jay","gender":"Male","age":20}
],
}
If not, you're going to need a custom deserializer indeed.

Java class: limit instance variable to one of several possible values, depending on other instance variables

I am sorry for the vague question. I am not sure what I'm looking for here.
I have a Java class, let's call it Bar. In that class is an instance variable, let's call it foo. foo is a String.
foo cannot just have any value. There is a long list of strings, and foo must be one of them.
Then, for each of those strings in the list I would like the possibility to set some extra conditions as to whether that specific foo can belong in that specific type of Bar (depending on other instance variables in that same Bar).
What approach should I take here? Obviously, I could put the list of strings in a static class somewhere and upon calling setFoo(String s) check whether s is in that list. But that would not allow me to check for extra conditions - or I would need to put all that logic for every value of foo in the same method, which would get ugly quickly.
Is the solution to make several hundred classes for every possible value of foo and insert in each the respective (often trivial) logic to determine what types of Bar it fits? That doesn't sound right either.
What approach should I take here?
Here's a more concrete example, to make it more clear what I am looking for. Say there is a Furniture class, with a variable material, which can be lots of things, anything from mahogany to plywood. But there is another variable, upholstery, and you can make furniture containing cotton of plywood but not oak; satin furniture of oak but not walnut; other types of fabric go well with any material; et cetera.
I wouldn't suggest creating multiple classes/templates for such a big use case. This is very opinion based but I'll take a shot at answering as best as I can.
In such a case where your options can be numerous and you want to keep a maintainable code base, the best solution is to separate the values and the logic. I recommend that you store your foo values in a database. At the same time, keep your client code as clean and small as possible. So that it doesn't need to filter through the data to figure out which data is valid. You want to minimize dependency to data in your code. Think of it this way: tomorrow you might need to add a new material to your material list. Do you want to modify all your code for that? Or do you want to just add it to your database and everything magically works? Obviously the latter is a better option. Here is an example on how to design such a system. Of course, this can vary based on your use case or variables but it is a good guideline. The basic rule of thumb is: your code should have as little dependency to data as possible.
Let's say you want to create a Bar which has to have a certain foo. In this case, I would create a database for BARS which contains all the possible Bars. Example:
ID NAME FOO
1 Door 1,4,10
I will also create a database FOOS which contains the details of each foo. For example:
ID NAME PROPERTY1 PROPERTY2 ...
1 Oak Brown Soft
When you create a Bar:
Bar door = new Bar(Bar.DOOR);
in the constructor you would go to the BARS table and query the foos. Then you would query the FOOS table and load all the material and assign them to the field inside your new object.
This way whenever you create a Bar the material can be changed and loaded from DB without changing any code. You can add as many types of Bar as you can and change material properties as you goo. Your client code however doesn't change much.
You might ask why do we create a database for FOOS and refer to it's ids in the BARS table? This way, you can modify the properties of each foo as much as you want. Also you can share foos between Bars and vice versa but you only need to change the db once. cross referencing becomes a breeze. I hope this example explains the idea clearly.
You say:
Is the solution to make several hundred classes for every possible
value of foo and insert in each the respective (often trivial) logic
to determine what types of Bar it fits? That doesn't sound right
either.
Why not have separate classes for each type of Foo? Unless you need to define new types of Foo without changing the code you can model them as plain Java classes. You can go with enums as well but it does not really give you any advantage since you still need to update the enum when adding a new type of Foo.
In any case here is type safe approach that guarantees compile time checking of your rules:
public static interface Material{}
public static interface Upholstery{}
public static class Oak implements Material{}
public static class Plywood implements Material{}
public static class Cotton implements Upholstery{}
public static class Satin implements Upholstery{}
public static class Furniture<M extends Material, U extends Upholstery>{
private M matrerial = null;
private U upholstery = null;
public Furniture(M matrerial, U upholstery){
this.matrerial = matrerial;
this.upholstery = upholstery;
}
public M getMatrerial() {
return matrerial;
}
public U getUpholstery() {
return upholstery;
}
}
public static Furniture<Plywood, Cotton> cottonFurnitureWithPlywood(Plywood plywood, Cotton cotton){
return new Furniture<>(plywood, cotton);
}
public static Furniture<Oak, Satin> satinFurnitureWithOak(Oak oak, Satin satin){
return new Furniture<>(oak, satin);
}
It depends on what you really want to achieve. Creating objects and passing them around will not magically solve your domain-specific problems.
If you cannot think of any real behavior to add to your objects (except the validation), then it might make more sense to just store your data and read them into memory whenever you want. Even treat rules as data.
Here is an example:
public class Furniture {
String name;
Material material;
Upholstery upholstery;
//getters, setters, other behavior
public Furniture(String name, Material m, Upholstery u) {
//Read rule files from memory or disk and do all the checks
//Do not instantiate if validation does not pass
this.name = name;
material = m;
upholstery = u;
}
}
To specify rules, you will then create three plain text files (e.g. using csv format). File 1 will contain valid values for material, file 2 will contain valid values for upholstery, and file 3 will have a matrix format like the following:
upholstery\material plywood mahogany oak
cotton 1 0 1
satin 0 1 0
to check if a material goes with an upholstery or not, just check the corresponding row and column.
Alternatively, if you have lots of data, you can opt for a database system along with an ORM. Rule tables then can be join tables and come with extra nice features a DBMS may provide (like easy checking for duplicate values). The validation table could look something like:
MaterialID UpholsteryID Compatability_Score
plywood cotton 1
oak satin 0
The advantage of using this approach is that you quickly get a working application and you can decide what to do as you add new behavior to your application. And even if it gets way more complex in the future (new rules, new data types, etc) you can use something like the repository pattern to keep your data and business logic decoupled.
Notes about Enums:
Although the solution suggested by #Igwe Kalu solves the specific case described in the question, it is not scalable. What if you want to find what material goes with a given upholstery (the reverse case)? You will need to create another enum which does not add anything meaningful to the program, or add complex logic to your application.
This is a more detailed description of the idea I threw out there in the comment:
Keep Furniture a POJO, i.e., just hold the data, no behavior or rules implemented in it.
Implement the rules in separate classes, something along the lines of:
interface FurnitureRule {
void validate(Furniture furniture) throws FurnitureRuleException;
}
class ValidMaterialRule implements FurnitureRule {
// this you can load in whatever way suitable in your architecture -
// from enums, DB, an XML file, a JSON file, or inject via Spring, etc.
private Set<String> validMaterialNames;
#Overload
void validate(Furniture furniture) throws FurnitureRuleException {
if (!validMaterialNames.contains(furniture.getMaterial()))
throws new FurnitureRuleException("Invalid material " + furniture.getMaterial());
}
}
class UpholsteryRule implements FurnitureRule {
// Again however suitable to implement/config this
private Map<String, Set<String>> validMaterialsPerUpholstery;
#Overload
void validate(Furniture furniture) throws FurnitureRuleException {
Set<String> validMaterialNames = validMaterialsPerUpholstery.get(furniture.getUpholstery();
if (validMaterialNames != null && !validMaterialNames.contains(furniture.getMaterial()))
throws new FurnitureRuleException("Invalid material " + furniture.getMaterial() + " for upholstery " + furniture.getUpholstery());
}
}
// and more complex rules if you need to
Then have some service along the lines of FurnitureManager. It's the "gatekeeper" for all Furniture creation/updates:
class FurnitureManager {
// configure these via e.g. Spring.
private List<FurnitureRule> rules;
public void updateFurniture(Furniture furniture) throws FurnitureRuleException {
rules.forEach(rule -> rule.validate(furniture))
// proceed to persist `furniture` in the database or whatever else you do with a valid piece of furniture.
}
}
material should be of type Enum.
public enum Material {
MAHOGANY,
TEAK,
OAK,
...
}
Furthermore you can have a validator for Furniture that contains the logic which types of Furniture make sense, and then call that validator in every method that can change the material or upholstery variable (typically only your setters).
public class Furniture {
private Material material;
private Upholstery upholstery; //Could also be String depending on your needs of course
public void setMaterial(Material material) {
if (FurnitureValidator.isValidCombination(material, this.upholstery)) {
this.material = material;
}
}
...
private static class FurnitureValidator {
private static boolean isValidCombination(Material material, Upholstery upholstery) {
switch(material) {
case MAHOGANY: return upholstery != Upholstery.COTTON;
break;
//and so on
}
}
}
}
We often are oblivious of the power inherent in enum types. The Java™ Tutorials clearly states "you should use enum types any time you need to represent a fixed set of constants."
How do you simply make the best of enum in resolving the challenge you presented? - Here goes:
public enum Material {
MAHOGANY( "satin", "velvet" ),
PLYWOOD( "leather" ),
// possibly many other materials and their matching fabrics...
OAK( "some other fabric - 0" ),
WALNUT( "some other fabric - 0", "some other fabric - 1" );
private final String[] listOfSuitingFabrics;
Material( String... fabrics ) {
this.listOfSuitingFabrics = fabrics;
}
String[] getListOfSuitingFabrics() {
return Arrays.copyOf( listOfSuitingFabrics );
}
public String toString() {
return name().substring( 0, 1 ) + name().substring( 1 );
}
}
Let's test it:
public class TestMaterial {
for ( Material material : Material.values() ) {
System.out.println( material.toString() + " go well with " + material.getListOfSuitingFabrics() );
}
}
Probably the approach I'd use (because it involves the least amount of code and it's reasonably fast) is to "flatten" the hierarchical logic into a one-dimensional Set of allowed value combinations. Then when setting one of the fields, validate that the proposed new combination is valid. I'd probably just use a Set of concatenated Strings for simplicity. For the example you give above, something like this:
class Furniture {
private String wood;
private String upholstery;
/**
* Set of all acceptable values, with each combination as a String.
* Example value: "plywood:cotton"
*/
private static final Set<String> allowed = new HashSet<>();
/**
* Load allowed values in initializer.
*
* TODO: load allowed values from DB or config file
* instead of hard-wiring.
*/
static {
allowed.add("plywood:cotton");
...
}
public void setWood(String wood) {
if (!allowed.contains(wood + ":" + this.upholstery)) {
throw new IllegalArgumentException("bad combination of materials!");
}
this.wood = wood;
}
public void setUpholstery(String upholstery) {
if (!allowed.contains(this.wood + ":" + upholstery)) {
throw new IllegalArgumentException("bad combination of materials!");
}
this.upholstery = upholstery;
}
public void setMaterials(String wood, String upholstery) {
if (!allowed.contains(wood + ":" + upholstery)) {
throw new IllegalArgumentException("bad combination of materials!");
}
this.wood = wood;
this.upholstery = upholstery;
}
// getters
...
}
The disadvantage of this approach compared to other answers is that there is no compile-time type checking. For example, if you try to set the wood to plywoo instead of plywood you won’t know about your error until runtime. In practice this disadvantage is negligible since presumably the options will be chosen by a user through a UI (or through some other means), so you won’t know what they are until runtime anyway. Plus the big advantage is that the code will never have to be changed so long as you’re willing to maintain a list of allowed combinations externally. As someone with 30 years of development experience, take my word for it that this approach is far more maintainable.
With the above code, you'll need to use setMaterials before using setWood or setUpholstery, since the other field will still be null and therefore not an allowed combination. You can initialize the class's fields with default materials to avoid this if you want.

getting the values of array from javascript to java as list

var catids = new Array();
I have a catids array where i store the checked checkbox values like the below one.
cat = $("input[name=catChkBox]:checked").map(function () {
return $(this).data('name');
}).get().join(",");
the cat variable forms something like this 1,2,3..
I want to send this "cat" to a java method and print those values.
I pass the values to java through a dwr call like this
DataHandler.getTasks( categories, {callback:function(data){
}, errorHandler:function(){
},async:false
});
I have configured dwr for pojo. should I configure anything for parameters?
I tried the below code but I didn't get anything.
public List<Facade> getTasks(String myIds){
String[] ids = catids .split(",");
System.out.println("-------size of cat id------------" + myIds.length);
for (int i=0; i<myIds.length;i++)
System.out.println(myIds[i]);
//finally it will return a pojo which i l be receiving it in data of dwr call.
-------size of cat id------------ is 1
myIds[i] prints nothing
I need it as an integer back.
What mistake am I doing ?
I will do it in this way.
JavaScript creates json object like this {"categoryIds": [1,2,3,4,5]}
Java converter convert json to java POJO object using for example Gson or Jackson library.
After convert you can work with java POJO object which have list of categories.
If you use this solution your code will be more clear and you will be able to share more objects between JavaScript and Java using the same clear solution.
Example (pseudo code)
CategorList class
public class CategoryList {
private ArrayList<Category> categoryList;
// getters and setters
}
Converter
public class CategoryListConverter {
public CategoryList convert(String json) {
Gson g = new Gson();
CategoryList cl = g.fromJson(json, CategoryList.class);
return cl;
}
}
I tried the code it workd fine
getTasks("1,2,3");
check what the value of categoriesIds is sent to getTask
Send this as a form parameter from webpage. Then get this from HttpServletRequest request object in java.
request.getParameter('categoryId');

How to convert arbitrary JSON into a usable structure in Java

I'm trying to use gson to convert this returned JSON into some kind of
data structure such that I can extract useful data.
For Example:
http://search.twitter.com/search.json?q=test&rpp=1
Returns:
{
"completed_in":0.028,
"max_id":196386333906837504,
"max_id_str":"196386333906837504",
"next_page":"?page=2&max_id=196386333906837504&q=test&rpp=1",
"page":1,
"query":"test",
"refresh_url":"?since_id=196386333906837504&q=test",
"results":[
{
"created_at":"Sat, 28 Apr 2012 23:52:05 +0000",
"from_user":"della_ky",
"from_user_id":525641596,
"from_user_id_str":"525641596",
"from_user_name":"kydella modeste",
"geo":null,
"id":196386333906837504,
"id_str":"196386333906837504",
"iso_language_code":"en",
"metadata":{
"result_type":"recent"
},
"profile_image_url":"http:\/\/a0.twimg.com\/profile_images\/2159990525\/webcam-toy-photo3_20_2__normal.jpg",
"profile_image_url_https":"https:\/\/si0.twimg.com\/profile_images\/2159990525\/webcam-toy-photo3_20_2__normal.jpg",
"source":"<a href="http:\/\/mobile.twitter.com" rel="nofollow">Mobile Web<\/a>",
"text":"RT #Y__U__NOOO: #SongsIKnowOffByHeart ALL SONGS I LISTEN TO. BRAIN, Y U NO REMEMBER TEST ANSWERS LIKE THAT?!?",
"to_user":null,
"to_user_id":null,
"to_user_id_str":null,
"to_user_name":null
}
],
"results_per_page":1,
"since_id":0,
"since_id_str":"0"
}
Ultimately, I would like to be able to output a list of tweets with the
name of the sender and the date/time of the tweet.
I have read through the gson documentation but it's going over my head
to be honest - lots of new concepts there for me.
Do I need to define a class which maps exactly to the structure of the
JSON in order to then populate an instance of that class? If so this
seems very inflexible/laborious. Ideally I'm looking for something
which will handle JSON in any form and give me a structure I can use
automatically...
Is anyone able to give me some pointers? Being new to this - the more
detailed and in words of the fewest syllables the better!
Update - Thanks to the responses I've already had on this I've had a go at putting a class together to capture the twitter JSON. However, since the JSON has an embedded ArrayList of Objects I'm struggling a bit... So far I have
public class tweetData {
private double completed_in;
private long max_id;
private long max_id_str;
private String next_page;
private int page;
private String query;
private String refresh_url;
private List<tweetDetails> tweets = new ArrayList<tweetDetails>();
}
and
public class tweetDetails {
private String created_at;
private String from_user;
private long from_user_id;
private long from_user_id_str;
private String from_user_name;
private String geo;
private long id;
private long id_str;
private String iso_language_code;
// "metadata":
// {
// "result_type":"recent"
// },
private String profile_image_url;
private String profile_image_url_https;
private String source;
private String text;
private String to_user;
private String to_user_id;
private String to_user_id_str;
private String to_user_name;
}
Which I'm instantiating with
URI uri = new URI("http", "search.twitter.com", "/search.json", "q="+ searchTerms + "&rrp=" + RRP, null);
URL twitterSearch = uri.toURL();
URLConnection yc = twitterSearch.openConnection();
JsonReader reader = new JsonReader(new InputStreamReader(yc.getInputStream()));
Gson gson = new Gson();
tweetData data = gson.fromJson(reader, tweetData.class);
System.out.println(data);
The basic name:values are being populated correctly but the ArrayList is not.
tweetData : 0.17196614959919140865196614959919140865?page=2&max_id=196614959919140865&q=test1test?since_id=196614959919140865&q=testSIZE 0[]
So, I'm still struggling a bit - any more tips hugely appreciated!
Tia,
Tom
Do I need to define a class which maps exactly to the structure of the JSON in order to then populate an instance of that class? If so this seems very inflexible/laborious.
Yes. GSON is a library that can be used to convert Java Objects into their JSON representation. It can also be used to convert a JSON string to an equivalent Java object. This is really powerful because you can automagically instantiate your Java objects from the JSON representation. Assuming your JSON doesn't change its structure, you only have to define the appropriate Java object representation once.
Ideally I'm looking for something which will handle JSON in any form and give me a structure I can use automatically...
However, if you don't want automagical serialisation/deserialisation, then try looking at a simpler library such as java.net/projects/jsonp.
You can extract stuff from it just by querying the keys:
final JSONObject json = new JSONObject(theJsonString);
final String id = json.getString("max_id");
final JSONArray results = json.getJSONArray("results");
final String user = results.getJSONObject(2).getString("from_user");
Gson actually does all the serialization for you. So yes, you would have to write the classes yourself. To you, this seams inflexible and laborious, but that's only because that library isn't made for what you're asking for (it doesn't parse 'arbitrary' JSON).
I would suggest at least considering writing the classes and using gson. The reason I say that is because either way your application's logic will have to expect a very specific format, and writing out that format in a Java class will make things tidier. Here's a nice guide that will help you get started that way.
If you want to simply decode the JSON without serializing it into a Java class (IMHO the only way to use 'arbitrary' JSON), you'll want to use another library. Try this one. It allows you to decode the JSON, and use it by getting values from it (as described in this question: Convert a JSON string to object in Java ME?).
There are some tools that do gson to schema mapping. You give some sample JSON responses, and the java classes to access them are created for you.
http://www.jsonschema2pojo.org/
Gson is a slick beast!
Or at least it became so over the years that have passed since the question had been asked.
You can pass it an Object.class as a second parameter to the fromJson() method and it will parse your Json into a reasonable structure of LinkedTreeMaps and ArrayLists.
Object result = (new Gson()).fromJson(jsonString, Object.class)
More than that, you can really do partial parsing and leave loose ends at any level of your object structure by defining a certain field as Object!
Gson will then parse Json into your structure and your field of type Object will contain the above mentioned structure of LinkedTreeMaps and ArrayLists.
E.g., you may define a class
Person {
String name;
Object details;
}
(Imagine, you care mostly about the person's name but may want the details also somewhere. To log them, for instance.)
Then you can pass the following Json to the fromJson(input, Person.class) method as a first parameter
{
"name": "Carlsson",
"details": {
"address": "Stockholm",
"phones": [
"work": "233-322-233-322",
"home": "none"
]
}
}
The result will have the name field filled with "Carlsson" string and details field will contain a LinkedTreeMap with keys "address" and "phones", etc.

Categories