java - What is the reasoning on modelling a class to represent JSON data and do I need to? -
i have come across question on stackoverflow asks converting json java. answer shows class modelled represent json data object being created , don't understand why.
does object contain information after gson reads content or 1 key/value pair? if contains 1 key/value pair, i'm assuming need create multiple objects json have below can use loop iterate on , add values drop down menu?
{ "1": "annie", "2": "olaf", "3": "galio", "4": "twistedfate", "5": "xinzhao", "6": "urgot", "7": "leblanc", "8": "vladimir", "9": "fiddlesticks", "10": "kayle", "11": "masteryi", "12": "alistar", "13": "ryze", "14": "sion", "15": "sivir", "16": "soraka", "17": "teemo", "18": "tristana", "19": "warwick", "20": "nunu" }
essentially aiming is:
1) create list of names values.
2) sort list of names (as comes unsorted) in alphabetical order
3) loop through list , add each name drop down menu
4) when name in drop down menu selected, key associated value passed url receives more data.
sorry if unclear. i've spent couple of hours trying understand how elements json , display it, trying create list can use key display information name have had no luck except using for-each loop.
gson bean mapping solution
okay, have bit unusual json object; keys (the numbers in case) represent properties of contained object. that's workable, have understand that, example, when looking "annie" in json object, if use gson map "bean" class, we'll call data
(as in linked example), you'd have create data object so:
class data { private string _1; // ... private string _20; public string get1() { return _1; } public void set1(string _1) { this._1 = _1; } // ... public string get20() { return _20; } public void set20(string _20) { this._20 = _20; } }
and using data data = new gson().fromjson(myjsonstring, data.class);
on given string, you'd able find "annie" calling... uh... data.get1()
?
clearly, isn't good solution.
better solutions
since data doesn't follow typical format json object, have 2 options:
- if can, refactor json representation more verbose, better representation parsing.
- use different approach parse existing json.
solution 1: changing json representation
refactoring json result in object (preferably) this:
{ "champions" : [ { "index" : 1, "name" : "annie" }, { "index" : 2, "name" : "olaf" }, // ... ] }
this map couple of beans this:
class data { private list<champion> champions; // todo getters , setters } class champion { private int index; private string name; // todo getters , setters }
however, adds lot of unnecessary clutter json object, , isn't necessary 2 fields per champion (the name, , index).
you simplify further so:
{ "champions" : [ "annie", "olaf", // ... ] }
the bean class be:
class data { private list<string> champions; // todo getters , setters }
much simpler, still requires change json you're getting, in situations isn't possible. if used this, though, rid of "bean" class entirely, via:
list<string> champions = (list<string>) new gson().fromjson(myjsonstring, new typetoken<list<string>>(){}.gettype());
solution 2: changing how json parsed
the arguably better , cleaner solution change how json parsed.
the goal here (if understand correctly) parse json , spit out collection of strings representing each champion's name, accessible numeric index of champion in json representation.
as such, , because of way json object laid out simple mapping of strings strings, can use gson pipe directly map<string, object>
, so:
map<string, string> mappedvalues = new gson().fromjson(myjsonstring, map.class); string anniesname = mappedvalues.get("1"); // "annie" string olafsname = mappedvalues.get("2"); // "olaf" boolean hastwentyoneelements = mappedvalues.containskey("21"); // false
this shorter, requires no "bean" classes, , keeps original json representation. downside can't tell whether indices of each entry correct , consistent; ie. if types in wrong number, or deletes 1 of entries.
to container of keys, use mappedvalues.keyset()
, , container of key-value pairs, use mappedvalues.entryset()
, gives set<map.entry<string, string>>
. both of can iterated over, , may in random order (i'm not sure whether underlying map
implementation preserves insertion order or not).
to index given name (ie. champ
), you'd use similar following:
string index = null; (map.entry<string, string> entry : mappedvalues.entryset()) { if (champ.equals(entry.getvalue())) { index = entry.getkey(); break; } }
of course, you'd have check see if index
null after this, , handle appropriately, it's doable.
edit: @vempo's answer provides cleaner, more efficient lookup strategy means of inverting map (although answer written jackson, instead of gson); adaptation of gson follows (and yes, there vastly superior version in java-8, left out sake of availability):
public map<string, string> invertmap(map<string, string> input) { map<string, string> newmap = new linkedtreemap<string, string>(); // todo pick optimal storage class (map.entry<string, string> entry : input.entryset()) { newmap.put(entry.getvalue(), entry.getkey()); } return newmap; } // ... map<string, string> mappedvalues = invertmap(new gson().fromjson(myjsonstring, map.class)); string annieindex = mappedvalues.get("annie"); // "1" string olafindex = mappedvalues.get("olaf"); // "2"
it's worth noting sacrifices efficiency of constructing map building twice (once gson , once more invert), makes value lookup more efficient.
Comments
Post a Comment