I am a newbie to android and I have a JSON file with dynamic key like this:
{
"x": {
"a": {
"1": [1,2,3,4],
"2": [1,2,3,4]
},
"b": {
"1": [1,2,3,4],
"2": [1,2,3,4]
}
},
"y": {
"a": {
"1": [1,2,3,4],
"2": [1,2,3,4]
},
"b": {
"1": [1,2,3,4],
"2": [1,2,3,4]
}
},
"z": {
"a": {
"1": [1,2,3,4],
"2": [1,2,3,4]
},
"b": {
"1": [1,2,3,4],
"2": [1,2,3,4]
}
}
}
I parsed it successfully by JSONObject but I have to loop by keys Iterator on x, y, z. For each time, I have to loop on a, b and the same for "1" and "2". I think it's not a good solution. I created models for them like this:
Class XYZ {
private String name; // "x", "y", "z" value
private ArrayList<ABC> abcList;
}
Class ABC {
private String name; // "a", "b", "c"
private ArrayList<Item> itemList;
}
Class Item{
private String ID; // "1", "2"
private int[] valueArray;
}
Can anyone help me to parse this json by Gson, I think it looks more professional :D. Thank you so much
Your models cannot map your JSON just because Gson default configuration clearly gets them unmatched.
You can have two "default" ways:
static
... since you didn't really mention why your JSON is considered dynamic:
final class XYZ {
final ABC x = null;
final ABC y = null;
final ABC z = null;
}
final class ABC {
final OneTwo a = null;
final OneTwo b = null;
}
final class OneTwo {
#SerializedName("1")
final List<Integer> one = null;
#SerializedName("2")
final List<Integer> two = null;
}
Example:
try ( final Reader reader = getPackageResourceReader(Q43695739.class, "dynamic.json") ) {
final XYZ xyz = gson.fromJson(reader, XYZ.class);
System.out.println(xyz.x.b.two);
}
dynamic (by deserialization)
... assuming your keys are dynamic, but the structure remains the same:
private static final Type stringToStringToStringToIntegerListType = new TypeToken<Map<String, Map<String, Map<String, List<Integer>>>>>() {
}.getType();
try ( final Reader reader = getPackageResourceReader(Q43695739.class, "dynamic.json") ) {
final Map<String, Map<String, Map<String, List<Integer>>>> m = gson.fromJson(reader, stringToStringToStringToIntegerListType);
System.out.println(m.get("x").get("b").get("2"));
}
dynamic (by JSON trees)
Another true dynamic approach that may be helpful for some scenarios. Also note that JSONObject is not in the Gson realm: you probably might have imported this one from the org.json package. Gson uses camel-cased names like JsonElement, JsonObject, etc.
try ( final Reader reader = getPackageResourceReader(Q43695739.class, "dynamic.json") ) {
final JsonElement jsonElement = gson.fromJson(reader, JsonElement.class)
.getAsJsonObject()
.getAsJsonObject("x")
.getAsJsonObject("b")
.getAsJsonArray("2");
System.out.println(jsonElement);
}
The first and the second examples produce java.util.List instances
[1, 2, 3, 4]
The third example returns a JsonArray instance with a slightly different toString implementation:
[1,2,3,4]
Related
I have a nested Json that I need to remove some objects in array with a filter, this in a dynamic way, this Json structure is not the same all time, for example:
{
"A": "HI",
"B": 1,
"C": [
{
"TIME": "TODAY",
"LOCATION": "USA",
"BALANCE": 100,
"STATE": "TX",
"NAME": "JHON"
},
{
"TIME": "YESTERDAY",
"LOCATION": "USA",
"BALANCE": 100,
"STATE": "TX",
"NAME": "MICHAEL"
},
{
"TIME": "YESTERDAY",
"LOCATION": "USA",
"BALANCE": 100,
"STATE": "TX",
"NAME": "REBECCA"
}
]
}
And now, from this kind of nested Json I want to remove the Object that contains key "NAME" with VALUE "Michael", and the result have to be this one:
{
"A": "HI",
"B": 1,
"C": [
{
"TIME": "TODAY",
"LOCATION": "USA",
"BALANCE": 100,
"STATE": "TX",
"NAME": "JHON"
},
{
"TIME": "YESTERDAY",
"LOCATION": "USA",
"BALANCE": 100,
"STATE": "TX",
"NAME": "REBECCA"
}
]
}
This JSON change every time depending on reponse from an API, just I have to match KEY - VALUE to remove the Object that I need filter without modify the Json structure, in this case I need to recive KEY = "NAME" and VALUE = "Michael" to filter this object.
In this case "C" is a variable key and I could have more keys with arrays in the same json that need to be filtered, I need a dynamic way to filter in array of objects based just in key-value
Could you help me find a way to perform this functionality?
Here is a streaming solution that can deal with huge responses without any significant impact on your system. It also does not require any class mappings using the built-in JSON node representation (therefore saving time and probably memory on type bindings).
public static void filterAbcBySpecializedStreaming(final JsonReader input, final JsonWriter output)
throws IOException {
input.beginObject();
output.beginObject();
// iterating over each entry of the outer object
while ( input.hasNext() ) {
final String name = input.nextName();
output.name(name);
switch ( name ) {
// assuming "A" is known to be a string always
case "A":
output.value(input.nextString());
break;
// assuming "B" is known to be a number always
case "B":
// note: JsonReader does not allow to read a number of an arbitrary length as an instance of `java.lang.Number`
output.value(new LazilyParsedNumber(input.nextString()));
break;
// assuming "C" is known to be an array of objects always
case "C":
input.beginArray();
output.beginArray();
// iterating over each element of the array
while ( input.hasNext() ) {
// assuming array elements are not very big and it trusts their size
final JsonObject jsonObject = Streams.parse(input)
.getAsJsonObject();
// if the current element JSON object has a property named "NAME" and its value is set to "MICHAEL", the skip it
// of course, this can also be externalized using the Strategy design pattern (e.g. using java.lang.function.Predicate)
// but this entire method is not that generic so probably it's fine
if ( jsonObject.get("NAME").getAsString().equals("MICHAEL") ) {
continue;
}
Streams.write(jsonObject, output);
}
input.endArray();
output.endArray();
break;
default:
throw new IllegalStateException("Unknown: " + name);
}
}
input.endObject();
output.endObject();
}
The test:
final JsonElement expected = Streams.parse(expectedJsonReader);
final ByteArrayOutputStream buffer = new ByteArrayOutputStream();
final JsonWriter output = new JsonWriter(new OutputStreamWriter(buffer));
Filter.filterAbcBySpecializedStreaming(input, output);
output.flush();
final JsonElement actual = JsonParser.parseReader(new InputStreamReader(new ByteArrayInputStream(buffer.toByteArray())));
Assertions.assertEquals(expected, actual);
Of course, it's not that easy, but it may result in the best performance. Making it generic and "dynamic" is an option, and it can be done according to your needs. If you find it too complex, the input JSON document is known not to be very big (therefore causing OutOfMemoryErrors), you can also filter it out as a tree, but again without any type bindings:
public static void filterAbcBySpecializedTree(final JsonElement input, final JsonElement output) {
final JsonObject inputJsonObject = input.getAsJsonObject();
final JsonObject outputJsonObject = output.getAsJsonObject();
for ( final Map.Entry<String, JsonElement> e : inputJsonObject.entrySet() ) {
final String name = e.getKey();
final JsonElement value = e.getValue();
switch ( name ) {
case "A":
case "B":
outputJsonObject.add(name, value.deepCopy());
break;
case "C":
final JsonArray valueJsonArray = value.getAsJsonArray()
.deepCopy();
for ( final Iterator<JsonElement> it = valueJsonArray.iterator(); it.hasNext(); ) {
final JsonObject elementJsonObject = it.next().getAsJsonObject();
if ( elementJsonObject.get("NAME").getAsString().equals("MICHAEL") ) {
it.remove();
}
}
outputJsonObject.add(name, valueJsonArray);
break;
default:
throw new IllegalStateException("Unknown: " + name);
}
}
}
Test:
final JsonElement input = Streams.parse(inputJsonReader);
final JsonElement expected = Streams.parse(expectedJsonReader);
final JsonElement actual = new JsonObject();
Filter.filterAbcBySpecializedTree(input, actual);
Assertions.assertEquals(expected, actual);
I have 3 JSON payloads: A, B and C. I need to find out if payload A contains B and C.
When B is compared against A, I need the result to be true.
When C is compared against A, I need the result to be false
as college key does not exist in payload A.
Note: Below JSON payloads are just examples and can have different structure and values.
Payload A:
{
"id": "1",
"search": {
"name": "Testing",
"age": "25",
"school": "sacred heart"
},
"Address": {
"street": "Abcedf",
"road": "4th cross",
"city": "bangalore"
}
}
Payload B
{
"id": "1",
"search": {
"name": "Testing"
},
"Address": {
"street": "Abcedf",
"road": "4th cross"
}
}
Payload C
{
"id": "1",
"search": {
"name": "Testing",
"college": "global"
},
"Address": {
"street": "Abcedf",
"road": "4th cross"
}
}
First step would be to create Java objects from your Json. You can use Gson, Jackson or any other library of your choice. For this example I have used Gson.
Your class definition
private class JsonObject {
String id;
Map search;
#SerializedName(value="Address")
Map address;
// getter and setter
}
Parsing objects ( a,b and c are your Json Strings )
Gson gson = new Gson();
JsonObject objectA = gson.fromJson( a, JsonObject.class);
JsonObject objectB = gson.fromJson( b, JsonObject.class);
JsonObject objectC = gson.fromJson( c, JsonObject.class);
For the actual comparison there are many ways to accomplish what you need. One way would be to implement your own method for comparing the objects
private static boolean check( JsonObject firstObject, JsonObject secondObject ) {
return firstObject.getAddress().entrySet().containsAll( secondObject.getAddress().entrySet() )
&& firstObject.getSearch().entrySet().containsAll( secondObject.getSearch().entrySet() );
}
Call the method
System.out.println( check ( objectA, objectB ) );
System.out.println( check ( objectA, objectC ) );
Result
true
false
Your can, of course create your own logic to compare your Maps inside the objects. Take a look at Java Collections Framework or the Guava library which offers some handy utility methods for working with Collections.
You can utilise JsonPointer to implement contains method. A.contains(B) method returns true when object A contains all pointers which has object B and all values are the same. Two objects (A and B) are equal when A.contains(B) and B.contains(A). From definition: A.contains(A) returns true for every A.
Simple implementation could look like below:
import com.fasterxml.jackson.core.JsonPointer;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
public class JsonPointersApp {
public static void main(String[] args) throws IOException {
File aJsonFile = new File("./resource/a.json").getAbsoluteFile();
File bJsonFile = new File("./resource/b.json").getAbsoluteFile();
File cJsonFile = new File("./resource/c.json").getAbsoluteFile();
ObjectMapper mapper = new ObjectMapper();
JsonPayload payloadA = new JsonPayload(mapper.readTree(aJsonFile));
JsonPayload payloadB = new JsonPayload(mapper.readTree(bJsonFile));
JsonPayload payloadC = new JsonPayload(mapper.readTree(cJsonFile));
System.out.println("JSON A:" + payloadA);
System.out.println("JSON B:" + payloadB);
System.out.println("JSON C:" + payloadC);
System.out.println("A contains B:" + payloadA.contains(payloadB));
System.out.println("A contains C:" + payloadA.contains(payloadC));
System.out.println("B contains C:" + payloadB.contains(payloadC));
System.out.println("C contains B:" + payloadC.contains(payloadB));
System.out.println("A contains A:" + payloadA.contains(payloadA));
}
}
class JsonPayload {
private final JsonNode root;
private final List<JsonPointer> pointers = new ArrayList<>();
JsonPayload(JsonNode root) {
this.root = Objects.requireNonNull(root);
findPointers(this.root, "");
}
public boolean contains(JsonPayload other) {
//If number of pointers are different, JSON payloads are also different
if (this.pointers.size() < other.pointers.size()) {
return false;
}
// If pointers are not the same
if (!this.pointers.containsAll(other.pointers)) {
return false;
}
// check values
return other.pointers
.stream()
.allMatch(p -> this.root.at(p).equals(other.root.at(p)));
}
private void findPointers(JsonNode node, String path) {
if (node.isObject()) {
node.fields().forEachRemaining(entry -> findPointers(entry.getValue(), path + "/" + entry.getKey()));
} else if (node.isArray()) {
final int size = node.size();
for (int i = 0; i < size; i++) {
findPointers(node.get(i), path + "/" + i);
}
} else {
pointers.add(JsonPointer.compile(path));
}
}
#Override
public String toString() {
return pointers.toString();
}
}
Above code prints:
JSON A:[/id, /search/name, /search/age, /search/school, /Address/street, /Address/road, /Address/city]
JSON B:[/id, /search/name, /Address/street, /Address/road]
JSON C:[/id, /search/name, /search/college, /Address/street, /Address/road]
A contains B:true
A contains C:false
B contains C:false
C contains B:true
A contains A:true
There is any data structure in java that i can work like a key value array from php or javascript?
$objectKeyTarget = "key2";
$array = array(
"key1": {
"id": 1,
"name" "exemple 1"
},
"key2": {
"id": 2,
"name" "exemple 2"
}
);
$dynamicObject = $array[$objectKeyTarget];
Well you could simply use a Map and a List for that. I'd suggest something like this:
public class Entry {
private int id;
private String name;
...
}
Map<String, List<Entry>> entriesByKey = new HashMap<>;
entriesByKey.put("key1", new Entry(1,"exemple 1"));
entriesByKey.put("key2", new Entry(2,"exemple 2"))
Log.d(..., entriesByKey.get("key2"));
If you are trying to store a JSON-Respone: There is in-built JSONObject in Android, so use that one instead.
The simple way
not_array = {
'Y':'YES',
'N':'NO',
'M': {'MY':'MAYBE YES','MN':'MAYBE NO'}
}
value = 'Y';
result = not_array[value];
console.log(result);
value1 = 'M';
value2 = 'MN';
result1 = not_array[value1][value2];
console.log(result1);
What I have: I am completely new to working with JSON files. I am currently using Gson to create two HashMaps<Key, List<Pair>> to store values into. I have gotten to the point where I can create a JSON file.
HashMap<Integer, List<Pair>> in = new HashMap<Integer, List<Pair>>();
HashMap<Integer, List<Pair>> out = new HashMap<Integer, List<Pair>>();
for (int i = 0; i < 300000, i++) {
List<Integer> inList = new ArrayList<Pair>();
List<Integer> outList = new ArrayList<Pair>();
in.put(i, inList);
out.put(i, outList);
}
gsonFile = new GsonBuilder().setPrettyPrinting().create();
FileWriter file = new FileWriter("newFile.json");
gsonFile.toJson(in, file);
gsonFile.toJson(out, file);
file.flush();
class Pair {
int id;
int value;
//Some code here
}
Which gives me to the following JSON file:
{
"0": [],
"1": [],
"2": [],
"3": [],
...
"300000": []
}
{
"0": [],
"1": [],
"2": [],
"3": [],
...
"300000": []
}
What I want: From here, I am wondering how I can use Gson to access for example List<Pair> for Key = 3 in the first HashMap to then add Pair objects into it, and then write back into the file. For example, something like:
List<Pair> list3 = gsonFile.get(0).get("3");
list3.add(new Pair(1, 1));
to become:
{
"0": [],
"1": [],
"2": [],
"3": [{"id": 1, "value": 1}],
...
"300000": []
}
{
"0": [],
"1": [],
"2": [],
"3": [],
...
"300000": []
}
I am using this method to store values as I am expected to have anywhere between 0 to 300,000 Pair objects in a single List<Pair> and therefore, keeping in and out on memory would result in out of memory errors.
I have a JSON record that looks like this:
{"ActionRecord": {
"101": {
"Desc": "string 1",
"Done": 1,
"MaxTimes": 2,
"Point": 30,
"Times": 4
},
"102": {
"Desc": "string 2",
"Done": 1,
"MaxTimes": 3,
"Point": 15,
"Times": 13
},
"103": {
"Desc": "string 3.",
"Done": 1,
"MaxTimes": 5,
"Point": 15,
"Times": 24
}, ... }
I can get Jackson to parse this if i create a hacky intermediate class that contains a field for each number, and then use something like this in the class:
#JsonProperty( value = "101" )
public MyClass hundred_one;
#JsonProperty( value = "102" )
public MyClass hundred_two;
#JsonProperty( value = "103" )
public MyClass hundred_three;
But I have to type out all the expected values, so it would be much easier to be able to use an array list of objects, and insert the numeric id into the POJO with Jackson's mapper.
Is there a way have Jackson automatically map it into a class like this? :
public enum ActionRecord {
Something ( "101" ),
SomethingElse( "102" ),
AnotherSomething ( "103" ),
;
String _id;
EK_DailyTaskInfo_ActionRecord( String id )
{
_id = id;
}
public String getId()
{
return _id;
}
public String Desc; // "some string.",
public boolean Done; // 1,
public int Times; // 4
public int MaxTimes; // 2,
public int Point; // 30,
}
It does not have to be an enum this was just something I was trying before I gave up
Jackson can decode it into a Map<String, Record> for you, e.g.
public class Record {
public String Desc; // "some string.",
public boolean Done; // 1,
public int Times; // 4
public int MaxTimes; // 2,
public int Point; // 30,
}
public class ActionRecords {
public Map<String, Record> ActionRecord
}
well I am using GSON library in shown example, Android has it's own api to handle the JSON where a iterator is very useful, code is also available on github
What i want to propose is that you should read all the keys with respect to it's key's and get the JsonObject from there, you you have a JsonList which is not an array this is what you can do
JsonParser parser = new JsonParser();
JsonElement element = parser.parse(result); // result is your json data
JsonObject obj = element.getAsJsonObject();
System.out.println(obj.toString());
JsonObject jsonObject = obj.getAsJsonObject("ActionRecord"); // this will get the JsonObject with the key ActionRecord
System.out.println(jsonObject);
Set<Map.Entry<String, JsonElement>> stringSet = jsonObject.entrySet(); // this will map all the JsonObject with it's keys
for (Map.Entry<String, JsonElement> key :stringSet) {
System.out.println(jsonObject.getAsJsonObject(key.getKey()).toString());
}
Once you have the key with it's corresponding JSONObject, you can create populate your own type for that object.
well this is for Gson, you might want to look for equivalent of this in Jackson
output
{"Desc":"string 1","Done":1,"MaxTimes":2,"Point":30,"Times":4}
{"Desc":"string 2","Done":1,"MaxTimes":3,"Point":15,"Times":13}
{"Desc":"string 3.","Done":1,"MaxTimes":5,"Point":15,"Times":24}