Gson parsing string that has no key-value pairs - java

I'm trying to parse a string with Gson library but without success. Here is my string:
[["-1.816513","52.5487566"],["-1.8164913","52.548824"]]
the problem in this example is that there are no key-value pairs. I looked at other examples but all of them had key-value pairs and didn't look like my problem.

My solution to parse a list of list of strings.
package stackoverflow.answers;
import java.lang.reflect.Type;
import java.util.List;
import com.google.gson.Gson;
import com.google.gson.reflect.TypeToken;
public class GsonTest {
public static void main(String[] arg) {
Gson gson = new Gson();
String jsonOutput = "[[\"-1.816513\",\"52.5487566\"],[\"-1.8164913\",\"52.548824\"]]";
Type listType = new TypeToken<List<List<String>>>() {}.getType();
List<List<String>> strings = (List<List<String>>) gson.fromJson(jsonOutput, listType);
for(List<String> inner: strings){
for(String s: inner){
System.out.println(s);
}
}
}
}
But since values can be "thinked" also a doubles, you can parse them directly changing type into solution:
package stackoverflow.answers;
import java.lang.reflect.Type;
import java.util.List;
import com.google.gson.Gson;
import com.google.gson.reflect.TypeToken;
public class GsonTest {
public static void main(String[] arg) {
Gson gson = new Gson();
String jsonOutput = "[[\"-1.816513\",\"52.5487566\"],[\"-1.8164913\",\"52.548824\"]]";
Type listType = new TypeToken<List<List<Double>>>() {}.getType();
List<List<Double>> numbers = (List<List<Double>>) gson.fromJson(jsonOutput, listType);
for(List<Double> inner: numbers){
for(Double d: inner){
System.out.println(d);
}
}
}
}
Not important in the context, but for future references: Java 7, Gson 2.2.4

One solution, with raw types:
package com.stackoverflow.so18525283;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import java.util.List;
public class App {
private static final String INPUT = "[[\"-1.816513\",\"52.5487566\"],[\"-1.8164913\",\"52.548824\"]]";
public static void main(final String[] args) {
final Gson gson = new GsonBuilder().create();
final List<?> fromJson = gson.fromJson(INPUT, List.class);
if (fromJson != null && fromJson.size() > 0 && fromJson.get(0) instanceof List) {
System.out.println(((List<?>) fromJson.get(0)).get(0)); // this is a String
}
}
}
Another solution is to recreate a valid JSON object, same App as below but with:
public static void main(String[] args) {
final Gson gson = new GsonBuilder().create();
final Foo fromJson = gson.fromJson("{ data: " + INPUT + "}", Foo.class);
// TODO: check for null
System.out.println(fromJson.data.get(0).get(0)); // this is a Double
}
private static class Foo {
List<List<Double>> data;
}

Related

Remove dublicates value from json array in android

[{"product_name":"13X25","ask_size":0,"product_id":"5","category_id":"1","quantity":"10","image":null,"description":"","product_pdf":null,"head":"MARSHAL","price":"22.00","user_quantity":"2"},{"product_name":"14X25","ask_size":0,"product_id":"6","category_id":"1","quantity":"12","image":null,"description":"","product_pdf":null,"head":"MARSHAL","price":"23.00","user_quantity":"1"},{"product_name":"14X25 C","ask_size":0,"product_id":"2","category_id":"1","quantity":"23","image":null,"description":"","product_pdf":null,"head":"KANGARO","price":"22.00","user_quantity":"0"},{"product_name":"17X25 C","ask_size":0,"product_id":"1","category_id":"1","quantity":"18","image":null,"description":"","product_pdf":null,"head":"HOKO","price":"12.00","user_quantity":"0"}]
This is my JSON array
Where I want to remove a particular key if it is already exist
There is a key named "head": "MARSHAL", This Key comes two times but I want to fetch it only one time.
If it's coming then it must be removed automatically from other JSONObject, Not only for one key
it can be to other keys also if they get repeated then I want them only one time and after that add that JSON data to the list
How can I do it for android?
I want to show this data into cardView adapter
First of all, convert your JSON to List<Map<String, Object>>.
You can use ObhectMapper to do so.
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;
import java.io.File;
import java.io.IOException;
import java.util.*;
public class Test {
private static ObjectMapper mapper = new ObjectMapper();
public static void main(String[] args) throws IOException {
List<Map<String, String>> listOfMap = createListMap();
Map<String, List<String>> tempMap = new HashMap<>();
listOfMap.forEach(
map -> {
Set<String> keySet = new HashSet<>(map.keySet());
keySet.forEach(
key -> {
List<String> tempValue = tempMap.get(key);
if (Objects.nonNull(tempValue) && tempValue.contains(map.get(key))) {
map.remove(key);
} else {
tempMap.put(key, Arrays.asList(map.get(key)));
}
});
});
System.out.println(createListMap(listOfMap));
}
private static List<Map<String, String>> createListMap() throws IOException {
File jsonFile = new File("src/main/resources/input.json");
return mapper.readValue(jsonFile, new TypeReference<List<Map<String, String>>>() {});
}
private static String createListMap(List<Map<String, String>> listOfMap) throws IOException {
return mapper.writeValueAsString(listOfMap);
}
}
Below is the output for your input JSON.
[{"product_name":"13X25","ask_size":"0","product_id":"5","category_id":"1","quantity":"10","image":null,"description":"","product_pdf":null,"head":"MARSHAL","price":"22.00","user_quantity":"2"},{"product_name":"14X25","product_id":"6","quantity":"12","price":"23.00","user_quantity":"1"},{"product_name":"14X25
C","product_id":"2","quantity":"23","head":"KANGARO","price":"22.00","user_quantity":"0"},{"product_name":"17X25
C","product_id":"1","quantity":"18","head":"HOKO","price":"12.00"}]
May this serves your purpose.

Gson deserialization skip one quotation

I have to deserialise JSON. I use GSON library for this purpose. I am building a web application. The user fills up where he wants to fly, then the query is sent to the API and the result is returned.
Let us use an example of response:
{"success":true,"data":{"WAW":{"0":{"price":153,"airline":"LO","flight_number":678,"departure_at":"2019-08-05T17:40:00Z","return_at":"2019-08-20T14:35:00Z","expires_at":"2019-05-24T20:55:02Z"},"1":{"price":126,"airline":"A3","flight_number":881,"departure_at":"2019-11-21T11:00:00Z","return_at":"2019-11-26T16:05:00Z","expires_at":"2019-05-27T13:39:23Z"},"2":{"price":171,"airline":"KL","flight_number":900,"departure_at":"2019-09-12T02:40:00Z","return_at":"2019-09-18T17:30:00Z","expires_at":"2019-05-27T10:40:40Z"},"3":{"price":235,"airline":"B2","flight_number":972,"departure_at":"2019-06-12T07:20:00Z","return_at":"2019-06-18T17:30:00Z","expires_at":"2019-05-26T12:31:22Z"},"4":{"price":596,"airline":"TK","flight_number":422,"departure_at":"2019-06-20T00:10:00Z","return_at":"2019-06-24T13:05:00Z","expires_at":"2019-05-26T08:08:21Z"}}},"error":null,"currency":"EUR"}
I created a classes in this way: http://pojo.sodhanalibrary.com
The problem is that there is always the name of the place of arrival. In this case WAW = Warsaw.
gsonConvert.gson(output).getData().getWAW().getFirst().getAirline()
I want to avoid it because the place of arrival will depend on the choice of the user.
You should be able to simplify your data property to type: Map<String, Map<String, Flight>>. Where Flight POJO represents given flight. Model could look like below:
class FlightResponse {
private boolean success;
private Map<String, Map<String, Flight>> data;
private String error;
private String currency;
public Map<String, Flight> getFlatData() {
return data.entrySet()
.stream()
.collect(HashMap::new, (m, e) -> m.putAll(e.getValue()), Map::putAll);
}
// getters, setters, toString
}
class Flight {
private BigDecimal price;
private String airline;
#SerializedName("flight_number")
private int flightNumber;
#SerializedName("departure_at")
private String departureAt;
#SerializedName("return_at")
private String returnAt;
#SerializedName("expires_at")
private String expiresAt;
// getters, setters, toString
}
And example how to parse your JSON:
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.google.gson.annotations.SerializedName;
import java.io.File;
import java.io.FileReader;
import java.math.BigDecimal;
import java.util.HashMap;
import java.util.Map;
public class GsonApp {
public static void main(String[] args) throws Exception {
File jsonFile = new File("./resource/test.json").getAbsoluteFile();
Gson gson = new GsonBuilder().create();
FlightResponse flightResponse = gson.fromJson(new FileReader(jsonFile), FlightResponse.class);
flightResponse.getFlatData().forEach((k, v) -> {
System.out.println(k + " => " + v);
});
}
}
Above code prints:
0 => Flight{price=153, airline='LO', flightNumber=678, departureAt='2019-08-05T17:40:00Z', returnAt='2019-08-20T14:35:00Z', expiresAt='2019-05-24T20:55:02Z'}
1 => Flight{price=126, airline='A3', flightNumber=881, departureAt='2019-11-21T11:00:00Z', returnAt='2019-11-26T16:05:00Z', expiresAt='2019-05-27T13:39:23Z'}
2 => Flight{price=171, airline='KL', flightNumber=900, departureAt='2019-09-12T02:40:00Z', returnAt='2019-09-18T17:30:00Z', expiresAt='2019-05-27T10:40:40Z'}
3 => Flight{price=235, airline='B2', flightNumber=972, departureAt='2019-06-12T07:20:00Z', returnAt='2019-06-18T17:30:00Z', expiresAt='2019-05-26T12:31:22Z'}
4 => Flight{price=596, airline='TK', flightNumber=422, departureAt='2019-06-20T00:10:00Z', returnAt='2019-06-24T13:05:00Z', expiresAt='2019-05-26T08:08:21Z'}
See also:
Flatten a Map> to Map with stream and lambda

How to work around Gson serialization giving different results when using generic wildcards?

Consider this example:
static class BaseBean { String baseField = "base"; }
static class ChildBean extends BaseBean { String childField = "child"; }
static class BaseBeanHolder {
List <? extends BaseBean> beans;
public BaseBeanHolder(List<? extends BaseBean> beans) { this.beans = beans; }
}
static class ChildBeanHolder {
List <ChildBean> beans;
public ChildBeanHolder(List<ChildBean> beans) { this.beans = beans; }
}
#Test
public void mcve() {
BaseBeanHolder baseHolder = new BaseBeanHolder(singletonList(new ChildBean()));
System.out.println(new Gson().toJson(baseHolder));
ChildBeanHolder childHolder = new ChildBeanHolder(singletonList(new ChildBean()));
System.out.println(new Gson().toJson(childHolder));
}
It prints:
{"beans":[{"baseField":"base"}]}
{"beans":[{"childField":"child","baseField":"base"}]}
So, although both lists hold child objects, only the second holder results in the child fields being serialized to JSON.
I have seen other questions, like here but I wondering whether there are reasonable workarounds to achieve what I want.
In other words: is there a way to have such one "holder" class that accepts either BaseBeans or ChildBeans (the <? extends BaseBean> does that), and that also gives me the correct results when serialising instances with Gson into JSON strings?
( note: I can't use specific type adapters, as I have no control where that actual Gson instance is coming from and how it is configured in our environment )
Generally collection implementations "takes" type from collection field declaration - not from given item on the List/Set/etc. We need to write custom serialiser which for each item find serialiser and use it. Simple implementation:
class TypeAwareListJsonSeserializer implements JsonSerializer<List<?>> {
#Override
public JsonElement serialize(List<?> src, Type typeOfSrc, JsonSerializationContext context) {
if (src == null) {
return JsonNull.INSTANCE;
}
JsonArray array = new JsonArray();
for (Object item : src) {
JsonElement jsonElement = context.serialize(item, item.getClass());
array.add(jsonElement);
}
return array;
}
}
And this is how we can use it:
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.google.gson.JsonArray;
import com.google.gson.JsonElement;
import com.google.gson.JsonNull;
import com.google.gson.JsonSerializationContext;
import com.google.gson.JsonSerializer;
import com.google.gson.annotations.JsonAdapter;
import java.lang.reflect.Type;
import java.util.Arrays;
import java.util.List;
public class GsonApp {
public static void main(String[] args) throws Exception {
List<BaseBean> children = Arrays.asList(new BaseBean(), new ChildBean(), new ChildBean2());
BaseBeanHolder baseHolder = new BaseBeanHolder(children);
Gson gson = new GsonBuilder()
.setPrettyPrinting()
.create();
System.out.println(gson.toJson(baseHolder));
}
}
class BaseBean {
String baseField = "base";
}
class ChildBean extends BaseBean {
String childField = "child";
}
class ChildBean2 extends BaseBean {
int bean2Int = 356;
}
class BaseBeanHolder {
#JsonAdapter(TypeAwareListJsonSeserializer.class)
private List<? extends BaseBean> beans;
// getters, setters, toString
}
Above code prints:
{
"beans": [
{
"baseField": "base"
},
{
"childField": "child",
"baseField": "base"
},
{
"bean2Int": 356,
"baseField": "base"
}
]
}
EDIT
During serialisation we lose information about type which will be needed during deserialisation process. I developed simple type information which will be stored during serialisation and used in deserialisation. It could look like below:
class TypeAwareListJsonAdapter implements JsonSerializer<List<?>>, JsonDeserializer<List<?>> {
private final String typeProperty = "#type";
#Override
public JsonElement serialize(List<?> src, Type typeOfSrc, JsonSerializationContext context) {
if (src == null) {
return JsonNull.INSTANCE;
}
JsonArray array = new JsonArray();
for (Object item : src) {
JsonObject jsonElement = (JsonObject) context.serialize(item, item.getClass());
jsonElement.addProperty(typeProperty, item.getClass().getSimpleName());
array.add(jsonElement);
}
return array;
}
#Override
public List<?> deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException {
final Type elementType = $Gson$Types.getCollectionElementType(typeOfT, List.class);
if (json instanceof JsonArray) {
final JsonArray array = (JsonArray) json;
final int size = array.size();
if (size == 0) {
return Collections.emptyList();
}
final List<?> suites = new ArrayList<>(size);
for (int i = 0; i < size; i++) {
JsonObject jsonElement = (JsonObject) array.get(i);
String simpleName = jsonElement.get(typeProperty).getAsString();
suites.add(context.deserialize(jsonElement, getClass(simpleName, elementType)));
}
return suites;
}
return Collections.emptyList();
}
private Type getClass(String simpleName, Type defaultType) {
try {
// you can use mapping or something else...
return Class.forName("com.model." + simpleName);
} catch (ClassNotFoundException e) {
return defaultType;
}
}
}
The biggest problem is to how to map classes to JSON values. We can use class simple name or provide Map<String, Class> and use it. Now, we can use it as above. Example app prints now:
{
"beans": [
{
"baseField": "base",
"#type": "BaseBean"
},
{
"childField": "child",
"baseField": "base",
"#type": "ChildBean"
},
{
"bean2Int": 356,
"baseField": "base",
"#type": "ChildBean2"
}
]
}
BaseBean{baseField='base'}
ChildBean{baseField='base', childField='child'}
ChildBean2{baseField='base', bean2Int=356}
Gson is built in consideration of "I am going to be used to serialize" and "I am going to be used to deserialize".
There is no way to determine from raw JSON what the exact runtime type of a descendant of BaseBean is.
You can use RuntimeTypeAdapterFactory as described here - unfortunately it's not published with the base Gson module nor is it in Maven Central as described here. This will publish enough information with the JSON that'll allow Gson to deserialize it.
More of an addendum: I just figured that at least serialization works fine with arrays, so a simple workaround was to rework the holder:
static class BaseBeanHolder {
BaseBean[] beans;
public BaseBeanHolder(BaseBean... beans) { this.beans = beans; }
}

map() with JSON array

Not Looking for Jackson Solution.
List of Imports:
import java.io.File;
import java.io.FileReader;
import java.util.ArrayList;
import org.json.simple.JSONArray;
import org.json.simple.JSONObject;
import org.json.simple.parser.JSONParser;
import java.util.List;
import java.util.stream.Collectors;
JSON data:
{
"Name": "ABC.com",
"Developer": "Vishwa ratna",
"Project List": [
"Compnay: National",
"Compnay: Enterprise",
"Compnay: Alamo"
]
}
My program:
#SuppressWarnings("unchecked")
public static void main(String[] args)
{
File text = new File("R:/TestJSON.txt");
JSONParser parser = new JSONParser();
try {
Object obj = parser.parse(new FileReader("R:\\Desktop\\TestJSON.txt"));
JSONObject jsonObject = (JSONObject) obj;
List<String> list = new ArrayList<>();
String name = (String) jsonObject.get("Name");
System.out.println(name);
String author = (String) jsonObject.get("Developer");
System.out.println(author);
JSONArray companyList = (JSONArray) jsonObject.get("Project List");
companyList.stream().map(e->{if(e.equals("Compnay: National")); return e;}).collect(Collectors.toList());
list.forEach(System.out::println);
}
catch (Exception e)
{}
}
}
Everything is executing correctly except
companyList.stream().map(e->{if(e.equals("Compnay: National")); return e;}).collect(Collectors.toList());
list.forEach(System.out::println);
I can get the desired result another way around but i want to know why my map is not returning anything?? should it not return Compnay: National.
Result i am getting:
ABC.com
Vishwa ratna
I am expecting:
ABC.com
Vishwa ratna
Compnay: National
Edit: Even after using filter() as suggested by some people here i am unable to get the desired result.
One you have to use filter instead of map.
Second you have to assign the result of collect to list
Your code should be :
list = (List<String>) companyList.stream()
.filter(e-> e.equals("Compnay: National"))
.map(Object::toString)
.collect(Collectors.toList());
Another solution maybe is to after the filter collect toArray, thenyou convert the array to stream use map with Object::toString then collect to list, this can work also
list = Arrays.stream(
companyList.stream()
.filter(e -> e.equals("Compnay: National"))
.toArray())
.map(Object::toString)
.collect(Collectors.toList());
Why filter and not map
map alwayse return a result, so if you make a condition inside map you have to use else part, it works in your case because you have a typo, your condition not make any sinse, but why you use if inside a map, and you have filter where you can avoid conditions?
Stream<T> filter(Predicate<? super T> predicate)
Like you see filter took a predicate and return a new stream not a boolean like you said in your comment
Another solution using Jackson library
I'm not sure what library you are using to parse your Json file, but, it seems not the perfect way, I would suggest another solution using jackson library, so you have :
1- add to pom.xml the dependency :
<!-- https://mvnrepository.com/artifact/com.fasterxml.jackson.core/jackson-core -->
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-core</artifactId>
<version>2.9.8</version>
</dependency>
2- create a Job which is similar to your Json file like so :
class MyObject {
private String name;
private String developer;
private List<String> projectList;
public String getName() {
return name;
}
#JsonSetter("Name")
public void setName(String name) {
this.name = name;
}
public String getDeveloper() {
return developer;
}
#JsonSetter("Developer")
public void setDeveloper(String developer) {
this.developer = developer;
}
public List<String> getProjectList() {
return projectList;
}
#JsonSetter("Project List")
public void setProjectList(List<String> projectList) {
this.projectList = projectList;
}
}
3- your code to parse
import com.fasterxml.jackson.annotation.JsonSetter;
import com.fasterxml.jackson.databind.MapperFeature;
import com.fasterxml.jackson.databind.ObjectMapper;
import java.io.File;
import java.io.IOException;
import java.util.List;
import java.util.stream.Collectors;
public class Mcve {
public static void main(String[] args) throws IOException {
String path = "R:\\Desktop\\TestJSON.txt";
ObjectMapper mapper = new ObjectMapper();
mapper.configure(MapperFeature.ACCEPT_CASE_INSENSITIVE_PROPERTIES, true);
MyObject obj = mapper.readValue(new File(path), MyObject.class);
String name = obj.getName();
System.out.println(name);
String author = obj.getDeveloper();
System.out.println(author);
List<String> list = obj.getProjectList()
.stream()
.filter(e -> e.equals("Compnay: National"))
.collect(Collectors.toList());
list.forEach(System.out::println);
}
}
Outputs
ABC.com
Vishwa ratna
Compnay: National
as you see, working with Objects is more easier and more helpful.

how to convert a string with square bracket into lots of doubles in Java?

I want to know that how to convert a string from Jsonarray, like the one in the following code, to a list of doubles.
String lineStringJsonArray = "[[[0.093493,51.6037],[0.092077,51.6134],[0.075051,51.6179],[-0.247248,51.5166],[-0.259754,51.5235],[-0.28098,51.518],[-0.301457,51.515]]]"
How should I use pattern to drop those square brackets?
Use json-simple.
Use the JSONArray object that returns an array-of-objects-like object to iterate over. I've compiled an example of printing all the doubles in the arrays, if that's what you wanted.
String lineStringJsonArray = "[[[0.093493,51.6037],[0.092077,51.6134],[0.075051,51.6179],[-0.247248,51.5166],[-0.259754,51.5235],[-0.28098,51.518],[-0.301457,51.515]]]";
JSONParser parser = new JSONParser();
try {
JSONArray arrays3 = (JSONArray) parser.parse(lineStringJsonArray);
JSONArray arrays2 = (JSONArray) arrays3.get(0);
for (Object items : arrays2) {
for (Object item : (JSONArray) items) {
System.out.println((Double) item);
}
}
} catch (ParseException e) {
e.printStackTrace();
}
If you want to decode this JSON string, I would advice you to use Jackson library (http://wiki.fasterxml.com/JacksonHome). You can find help on Google on how to import it and use it.
Then the class to use should look like this :
public class AwesomeClassName extends ArrayList<ArrayList<ArrayList<Double>>> {
}
Please ask if you need any help.
Promoting reuse, I Prefer the answer from #ReutSharabani. However to the original question, to use Pattern, here is another solution:
import java.util.ArrayList;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class Play {
static class Pair<T> {
T v1,v2;
Pair(T v1,T v2) { this.v1 = v1; this.v2 = v2; }
#Override public String toString() {
return "(X: "+v1+", Y: "+v2+")";
}
}
public static void main(String args[]) {
List<Pair<Double>> l = new ArrayList<>();
Pattern p = Pattern.compile("\\[([^\\[\\],]*),([^\\[\\],]*)]");
Matcher m = p.matcher("[[[0.093493,51.6037],[0.092077,51.6134],[0.075051,51.6179],[-0.247248,51.5166],[-0.259754,51.5235],[-0.28098,51.518],[-0.301457,51.515]]]");
while (m.find()) {
String v1 = m.group(1);
String v2 = m.group(2);
l.add(new Pair<>(Double.valueOf(v1),Double.valueOf(v2)));
}
l.stream().map(Pair::toString).forEach(System.out::println);
}
}

Categories