Json map into java map - java

I have the following json file
[{
"en": {
"key1": "Ap",
"key2": "ap2"
}
},
{
"ar": {
"key1": "Ap",
"key2": "ap2"
}
}
]
I would like to create a Map in Java such as the key is the language (like en or ar) and the value is a object. Something like this.
public class Category {
private String key1;
private String key2;
}
Type type = new TypeToken<Map<String, Category>>() {}.getType();
Gson gson = new Gson();
InputStream in = MyClass.class.getResourceAsStream("/categories.json");
String text = IOUtils.toString(in, StandardCharsets.UTF_8);
Map<String, Category> map = gson.fromJson(text, type);
But when I run this code, I get errors:
com.google.gson.JsonSyntaxException: java.lang.IllegalStateException: Expected BEGIN_ARRAY but was BEGIN_OBJECT at line 1 column 3 path $[0]
Is my Json structure wrong or is there an easier way to map this?

try to read this json file
{
"ar": {
"key1": "Ap",
"key2": "ap2"
},
"en": {
"key1": "Ap",
"key2": "ap2"
}
}

The above json is collection of JsonObject like list or array, so just parse it to List of Map objects
Type type = new TypeToken<List<Map<String, Category>>>() {}.getType();

Your json is a list of maps, not only maps. So you have to add it to the type declared.
Try this:
import com.google.gson.Gson;
import com.google.gson.reflect.TypeToken;
import java.lang.reflect.Type;
import java.util.List;
import java.util.Map;
public class Category {
private String key1;
private String key2;
#Override
public String toString() {
return "Category{" +
"key1='" + key1 + '\'' +
", key2='" + key2 + '\'' +
'}';
}
public static void main(String[] args) {
String json = "[{\n" +
" \"en\": {\n" +
" \"key1\": \"Ap\",\n" +
" \"key2\": \"ap2\"\n" +
" }\n" +
" },\n" +
"\n" +
" {\n" +
" \"ar\": {\n" +
" \"key1\": \"Ap\",\n" +
" \"key2\": \"ap2\"\n" +
" }\n" +
" }\n" +
"]";
Type type = new TypeToken<List<Map<String, Category>>>() {}.getType();
Gson gson = new Gson();
List<Map<String, Category>> maps = gson.fromJson(json, type);
System.out.println(maps);
}
}

Your input is an json array with two objects. However your target variable 'type' is a of type Object and not an 'Array of Objects'. In simpler terms, Map cannot store an
Array.
Lets take a simpler approach to this problem(not a recommended approach). If we convert the map manually to an array of maps, that would look like this:
yourJson -> [Map1(en,category1(Ap,ap2)),Map2(en,category2(Ap,ap2))]
i.e. An array of Maps
So in java equivalent this becomes:
Type typeOfT2 = new TypeToken<ArrayList<HashMap<String, Category>>>() {}.getType();
List<HashMap<String, Category>> list = gson.fromJson(text, typeOfT2);
We get to what we want, but there are better ways of doing this. We need Jackson instead of Gson for this.(Some one may add a Gson based solution, pretty sure a cleaner one than above exists). Here we will use ObjectMapper from com.fasterxml.jackson.databind.ObjectMapper
ObjectMapper om = new ObjectMapper();
List<Map.Entry<String, Category>> listx = om.readValue(text, ArrayList.class);
If you print listx. You can see this(overridden toString() of Category class):
[{en={key1=Ap, key2=ap2}}, {ar={key1=Ap, key2=ap2}}]
listx is the most accurate representation of your json and not a Map.
Now if you need a map, I will leave that as an exercise for you about how to convert your List of Map.Entry to a Map implementation.
PS.: First long answer here. Apologies for any mistakes.

Related

parse strange Json response to a list

I want to parse this object to a list of string. I do not need the key but just want the value as a list of string.
I cannot have a simple model classes because the keys object are more than 1000 in some responses and are random.
So please any idea how to parse it to list in kotlin or java?
{
"data": {
"21": "593754434425",
"22": "4560864343802",
"23": "7557134347529",
"24": "5937544344255",
"25": "45608643438024",
"26": "75571343475293"
}
}
You could first deserialize it as it is, and then convert to a list.
The JSON can be represented this way:
data class Response(val data: Map<String, String>)
You can mark this class #Serializable and use Kotlinx Serialization to deserialize it, or you can use other libraries like Moshi or Jackson (with jackson-module-kotlin).
Once it's deserialized, simply get the values of the map (it's a collection):
val response = Json.decodeFromString<Response>(yourJsonString)
// this is a Collection, not List, but it should be good enough
val stringValues = response.data.values
// if you really need a List<String>
val list = stringValues.toList()
If you want to get the values in the natural order of the keys, you can also use something like:
val values = response.data.toSortedMap(compareBy<String> { it.toInt() }).values
You can use this to parse your data:
val it: Iterator<String> = json.keys()
val arrayList = ArrayList<String>()
while (it.hasNext()) {
val key = it.next()
arrayList.add(json.get(key))
}
A better way is to change the json model, if you access it.
{
"data": [
"593754434425","4560864343802",
"7557134347529","5937544344255",
"45608643438024","75571343475293"
]
}
For this problem, its handy to use the libriary org.json.
See following code snippet:
import org.json.JSONObject;
import java.util.ArrayList;
public class Main {
public static void main(String[] args) {
// Defining the input
String input = "{\n" +
" \"data\": {\n" +
" \"21\": \"593754434425\",\n" +
" \"22\": \"4560864343802\",\n" +
" \"23\": \"7557134347529\",\n" +
" \"24\": \"5937544344255\",\n" +
" \"25\": \"45608643438024\",\n" +
" \"26\": \"75571343475293\"\n" +
" }\n" +
"}\n";
// Parsing it to a json object with org.json
JSONObject inputJson = new JSONObject(input);
// If inputJson does not contain the key data, we return
if(!inputJson.has("data")) return;
// Else we read this data object to a new JSONObject
JSONObject dataJson = inputJson.getJSONObject("data");
// Define an array list where all the values will be contained
ArrayList<String> values = new ArrayList<>();
// Get a key set of the dat json object. For each key we get its respective value and add it to our value array list
for (String key : dataJson.keySet()) values.add(dataJson.getString(key));
// Print all values
for (String value : values) System.out.println(value);
}
}
=>
4560864343802
7557134347529
5937544344255
45608643438024
75571343475293
593754434425
Installing org.json is the easiest with a package manager like maven or gradle.
Guys i have comeup with a similar solution for the problem here
this is my model class
data class UnVerifiedTagIds(
#SerializedName("data")
val data: Object
)
and this is how i parse the respone here
val values: ArrayList<String> = ArrayList()
val list_of_tag_ids: ArrayList<String> =response.data as ArrayList<String>
The ist one is the dataclass for the response
and the 2nd one is the ApiCallInterface m using Retrofit...
and the last one is the apicall itself
I am using Kotlin language
do class name with name like this data class Result(val data:Map<String,String>)
and using library GSON for convert string json to this model
val json = "{\n" +
" \"data\": {\n" +
" \"21\": \"593754434425\",\n" +
" \"22\": \"4560864343802\",\n" +
" \"23\": \"7557134347529\",\n" +
" \"24\": \"5937544344255\",\n" +
" \"25\": \"45608643438024\",\n" +
" \"26\": \"75571343475293\"\n" +
" }\n" +
"}"
val dat = Gson().fromJson(json,Result::class.java)
if (dat.data.isNotEmpty()){
val list= dat.data.values.toMutableList()
print(list)
}
that works fine with me

Get nested JSON elements from ObjectNode using Jackson

I have an ObjectNode, that looks as follows
{
"Header":{
"sub-header1":{
"#field":"value",
"#field":"value",
},
"sub-header2":{
"field":"",
"field":"",
"field":"",
"panel_field":{
"value":"",
"value":""
}
}
}
Now, what I want to do is to get all the fields from sub-headers in a list. This is the method that I'm employing
public static List<String> getDocumentFields(ObjectNode jsonDocumentNode) {
List<String> documentFields = new ArrayList<>();
Iterator<JsonNode> fields = jsonDocumentNode.elements();
while (fields.hasNext()) {
JsonNode jsonNode = fields.next();
Iterator<Map.Entry<String, JsonNode>> jsonFields = jsonNode.fields();
while (jsonFields.hasNext()) {
Map.Entry<String, JsonNode> jsonNodeEntry = jsonFields.next();
documentFields.add(jsonNodeEntry.getKey());
}
}
return documentFields;
}
But, I'm only getting headers in the list like {sub-header1, sub-header2}, instead of fields. How can I fix this? I'd really appreciate any kind of help.
EDIT:
While #sfiss's answer helped a great deal, I still wanted to find a way to do so without hardcoding the loop-logic and this answer turned out to be the exact thing I was looking for.
Well, it's simple, you are not iterating deep enough (field list is on third level). If you know the structure of your JSON, just iterate until you find the desired fields:
public class MyTest {
#Test
public void testJson() throws IOException {
final String json = getJson();
final JsonNode jsonDocumentNode = new ObjectMapper().readTree(json);
final List<String> fields = getDocumentFields((ObjectNode) jsonDocumentNode);
assertThat(fields, Matchers.contains("#field1", "#field2", "field1", "field2", "field3", "panel_field"));
}
public static String getJson() {
return "{\r\n" +
" \"Header\":{\r\n" +
" \"sub-header1\":{\r\n" +
" \"#field1\":\"value\",\r\n" +
" \"#field2\":\"value\"\r\n" +
" },\r\n" +
" \"sub-header2\":{\r\n" +
" \"field1\":\"\",\r\n" +
" \"field2\":\"\",\r\n" +
" \"field3\":\"\",\r\n" +
" \"panel_field\":{\r\n" +
" \"value1\":\"\",\r\n" +
" \"value2\":\"\"\r\n" +
" }\r\n" +
" } \r\n" +
" }\r\n" +
"}";
}
public static List<String> getDocumentFields(final ObjectNode jsonDocumentNode) {
final List<String> documentFields = new ArrayList<>();
for (final JsonNode header : (Iterable<JsonNode>) jsonDocumentNode::elements) {
for (final JsonNode subheader : (Iterable<JsonNode>) header::elements) {
for (final Map.Entry<String, JsonNode> field : (Iterable<Map.Entry<String, JsonNode>>) subheader::fields) {
documentFields.add(field.getKey());
}
}
}
return documentFields;
}
}
However, I would argue that it is simpler to let jackson serialize the JSON into a convenient data structure, and you just use the POJO's getters to obtain your values. That would also make it more clear than handling the JsonNode.
Just FYI, I edited your JSON slightly and used Java 8 SAM conversions to create iterables for the foreach-loops from the iterators, but you can still use your code and iterate one more level using while and iterators.

POJO with array of integers to deserialize

I am having some trouble deserializing the following JSON into a POJO. I have no control over the JSON structure, else I would've implemented it in some other way, but, that's life for you.
{
"1":{
"test":"1",
"other":"stuff"
},
"2":{
"test":"2",
"other":"stuff2"
}
}
Anyway, I am trying to deserialize by using a POJO with:
public Map<Integer, Payload> payload;
but although the Map does have a size of 2, when I try to get each of it, it's contents are null. Any idea on what I am doing wrong?
Thank you
I have no idea how the payload class looks like, but it should be something like this:
class Payload {
String test;
String other;
#Override
public String toString() {
return "Payload [test=" + test + ", other=" + other + "]";
}
}
If you assert this condition, then you can deserialize the json using a TypeToken> as token as danypata suggest... like:
public static void main(String args[]) throws InterruptedException {
String ff = "{\"1\":{" + "\"test\":\"1\"," + "\"other\":\"stuff\"" + "}," + "\"2\":{" + "\"test\":\"2\","
+ "\"other\":\"stuff2\"" + "}}";
Gson gson = new Gson();
Type mapType = new TypeToken<Map<String, Payload>>() {
}.getType();
Map<String, Payload> map = gson.fromJson(ff, mapType);
System.out.println(map);
for (Entry<String, Payload> entry : map.entrySet()) {
System.out.println(entry.getKey());
System.out.println(entry.getValue());
}
}
giving as result:
{1=Payload [test=1, other=stuff], 2=Payload [test=2, other=stuff2]}
1
Payload [test=1, other=stuff]
2
Payload [test=2, other=stuff2]
Why don't you use the Android JSONObject class? Then you can parse with it your entire JSON string and then you can obtain the values easily. For example this is to get the values of your "1" JSON object:
final JSONObject jsonObject = new JSONObject(jsonString);
final String test = jsonObject.getJSONObject("1").getString("test");
final String other = jsonObject.getJSONObject("1").getString("other");

Serialize Json into generic structure without schema using Java and Jackson

I have a need to serialize a JSON without being attached to particular schema for resulting object, e.g., to some generic set/map/hashmap.
As input, I have a string with a JSON. I do not know schema for that JSON.
As output, I want a Java Object such as Hashmap or similar that has key-value serialization of input.
Note that input JSON has both basic fields and Array/List inside it.
I have to use Java and Jackson (or some other library). How I possibly can do that?
Jackson data binding is able to read any json input into a Map with String key and Object value (that can be also a map or collection). You just tell the mapper that you would like to read the json into a map. You do that by giving the mapper the appropriate type reference:
import java.util.*;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;
public class Test
{
public static void main(String[] args)
{
try {
String json = "{ "
+ "\"string-property\": \"string-value\", "
+ "\"int-property\": 1, "
+ "\"bool-property\": true, "
+ "\"collection-property\": [\"a\", \"b\", \"c\"], "
+ "\"map-property\": {\"inner-property\": \"inner-value\"} "
+ "}";
ObjectMapper mapper = new ObjectMapper();
Map<String, Object> map = new HashMap<>();
// convert JSON string to Map
map = mapper.readValue(json, new TypeReference<Map<String, Object>>(){});
System.out.println("input: " + json);
System.out.println("output:");
for (Map.Entry<String, Object> entry : map.entrySet()) {
System.out.println("key: " + entry.getKey());
System.out.println("value type: " + entry.getValue().getClass());
System.out.println("value: " + entry.getValue().toString());
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
output:
input: { "string-property": "string-value", "int-property": 1, "bool-property": true, "collection-property": ["a", "b", "c"], "map-property": {"inner-property": "inner-value"} }
output:
key: string-property
value type: class java.lang.String
value: string-value
key: int-property
value type: class java.lang.Integer
value: 1
key: bool-property
value type: class java.lang.Boolean
value: true
key: collection-property
value type: class java.util.ArrayList
value: [a, b, c]
key: map-property
value type: class java.util.LinkedHashMap
value: {inner-property=inner-value}

How to convert list of json objects to pojo list of strings that contains some field from each object?

I have a JSON document that describe list of objects, it looks something like this:
[
{
"txId": "ffff",
"sender" : "0xwwwwwww",
"recepient" : "0xeferfef"
},
{
"txId": "ffff",
"sender" : "0xwwwwwww",
"recepient" : "0xeferfef"
}
...
...
]
How can I get List<String> that contains txId values from each object using only Jackson API (without converting this JSON to a list of pojo-objects then proceed this list by foreach and create new list of strings)?
You can always read a JSON document as JsonNode object with Jackson API (no need of creating POJO). Next, there are several ways of reading and manipulating the data represented as JsonNode object. One of the most convenient ways available from Java 8+ is to create a java.util.Stream<JsonNode> and collect the final list as a result of a mapping from JsonNode to String, where String is represents a value of node.txId field.
You can create java.util.Stream<JsonNode> with:
java.util.stream.StreamSupport.stream(jsonNode.spliterator(), false)
and then you can call map(node -> node.get("txId").textValue() and finally call collect() to terminate the stream and get your expected result.
Consider following code as an example (requires Java 8+):
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import java.io.IOException;
import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.StreamSupport;
final class JacksonReadExample {
public static void main(String[] args) throws IOException {
final String json = " [\n" +
" {\n" +
" \"txId\": \"ffff-001\",\n" +
" \"sender\" : \"0xwwwwwww\",\n" +
" \"recepient\" : \"0xeferfef\"\n" +
" },\n" +
" {\n" +
" \"txId\": \"ffff-002\",\n" +
" \"sender\" : \"0xwwwwwww\",\n" +
" \"recepient\" : \"0xeferfef\"\n" +
" }\n" +
"]";
final ObjectMapper mapper = new ObjectMapper();
final JsonNode jsonNode = mapper.readTree(json);
final List<String> ids = StreamSupport.stream(jsonNode.spliterator(), false)
.map(node -> node.get("txId").textValue())
.collect(Collectors.toList());
System.out.println(ids);
}
}
Output:
[ffff-001, ffff-002]

Categories