Convert json structure to array with Retrofit - java

I'm in trouble with Retrofit and a ugly json object in the Trakt.tv API:
{
"season": 1,
"episodes": {
"1": true,
"2": true,
"3": false,
"4": false,
"5": false,
"6": false,
"7": false
}
}
"episodes" content is obviously dynamic and I'd like to handle it as a simple Boolean array, like this:
int season;
Boolean[] episodes;
How to do that?

You can first convert the JSON string into Map<String,Object> then finally create the desired object.
sample code:
public class EpisodesDetail {
private int season;
private Boolean[] episodes;
// getter & setter
}
...
BufferedReader reader = new BufferedReader(new FileReader(new File("json.txt")));
Type type = new TypeToken<Map<String, Object>>() {}.getType();
Map<String, Object> map = new Gson().fromJson(reader, type);
EpisodesDetail geometry = new EpisodesDetail();
geometry.setSeason(((Double) map.get("season")).intValue());
geometry.setEpisodes(((Map<String, Boolean>) map.get("episodes")).values().toArray(
new Boolean[] {}));
System.out.println(new GsonBuilder().setPrettyPrinting().create().toJson(geometry));
output:
{
"season": 1,
"episodes": [
true,
true,
false,
false,
false,
false,
false
]
}
There is one more approach using GSON Deserialiser
sample code:
class EpisodesDetailDeserializer implements JsonDeserializer<EpisodesDetail> {
#Override
public EpisodesDetail deserialize(final JsonElement json, final Type typeOfT,
final JsonDeserializationContext context) throws JsonParseException {
EpisodesDetail geometry = new EpisodesDetail();
JsonObject jsonObject = json.getAsJsonObject();
int season = jsonObject.get("season").getAsInt();
geometry.setSeason(season);
List<Boolean> episodes = new ArrayList<Boolean>();
Set<Entry<String, JsonElement>> set = jsonObject.get("episodes").getAsJsonObject()
.entrySet();
Iterator<Entry<String, JsonElement>> it = set.iterator();
while (it.hasNext()) {
episodes.add(it.next().getValue().getAsBoolean());
}
geometry.setEpisodes(episodes.toArray(new Boolean[] {}));
return geometry;
}
}
BufferedReader reader = new BufferedReader(new FileReader(new File("json.txt")));
EpisodesDetail episodesDetail = new GsonBuilder()
.registerTypeAdapter(EpisodesDetail.class, new EpisodesDetailDeserializer())
.create().fromJson(reader, EpisodesDetail.class);
System.out.println(new GsonBuilder().setPrettyPrinting().create().toJson(episodesDetail));
See How do I write a custom JSON deserializer for Gson?

When I use jackson library to parse that JSON, I use ObjectMapper and DramaInfo class as follows.
package jackson;
import java.io.IOException;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
import com.fasterxml.jackson.databind.ObjectMapper;
class DramaInfo {
int season;
List<Boolean> episodes;
public void setSeason(int season) {
this.season = season;
}
public int getSeason() {
return this.season;
}
public List<Boolean> getEpisodes() {
return new LinkedList<>( this.episodes );
}
public void setEpisodes(Map<String, Boolean> o) {
// used Java 1.8 Stream.
// just see http://docs.oracle.com/javase/tutorial/collections/streams/reduction.html
episodes = o.keySet().stream().map(e -> o.get(e)).collect(Collectors.toList());
}
public String toString() {
String ret = "season: " + this.season + "\n";
ret += this.episodes.toString();
return ret;
}
}
public class LoadJsonData {
public static void main(String[] args) {
String toConvert = "{\"season\": 1, \"episodes\": { \"1\": true, \"2\": true, \"3\": false, \"4\": false, \"5\": false, \"6\": false, \"7\": false } }";
ObjectMapper mapper = new ObjectMapper();
try {
DramaInfo info = mapper.readValue(toConvert, DramaInfo.class);
System.out.println(info);
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
So, this is a suggestion because I have never used Retrofit. If you are to use Retrofit as follows, how about trying out the DramaInfo class shown above?
public interface DramaService {
#GET("/dramas/{drama}/info")
DramaInfo listRepos(#Path("drama") String drama);
}

Related

Avro logicalType String Date conversion to EPOCH timestamp-millis

I have below schema
{"name": "timestampstring", "type": [{"type":"string","logicalType":"timestamp-millis"}, "null"]},
I intend to supply date to it, and have conversion convert the date to epoch mili.
GenericRecord user2 = new GenericData.Record(schema1);
user2.put("timestampstring", "2019-01-26T12:00:40.931");
final GenericData genericData = new GenericData();
genericData.addLogicalTypeConversion(new MyTimestampConversion());
datumReader = new GenericDatumReader<GenericRecord>(schema2, schema2, genericData);
GenericRecord user = null;
try (DataFileReader<GenericRecord> dataFileReader = new DataFileReader<GenericRecord>(file1, datumReader)) {
while (dataFileReader.hasNext()) {
user = dataFileReader.next(user);
System.out.println(user);
}
}
//Conversion code
public static class MyTimestampConversion extends Conversion<Long> {
public MyTimestampConversion() {
}
public Class<Long> getConvertedType() {
return Long.class;
}
public String getLogicalTypeName() {
return "timestamp-millis";
}
public Long fromCharSequence(CharSequence value, Schema schema, LogicalType type) {
return 123L;
}
}
But this code doesnt work... I was expecting it to convert to timestamp milis (i hardcoded 123L in above sample).
Any help?
Referring back to How to define a LogicalType in Avro. (java), I managed to solve this by creating my own logical type. It seems like doing this with "timestamp-millis" logicalType wont work. So I created my own logicalType...
package example;
import org.apache.avro.Conversion;
import org.apache.avro.LogicalType;
import org.apache.avro.LogicalTypes;
import org.apache.avro.Schema;
import org.apache.avro.Schema.Parser;
import org.apache.avro.file.DataFileReader;
import org.apache.avro.file.DataFileWriter;
import org.apache.avro.generic.*;
import org.apache.avro.io.DatumReader;
import org.apache.avro.io.DatumWriter;
import org.apache.avro.io.ResolvingDecoder;
import org.joda.time.DateTime;
import java.io.File;
import java.io.IOException;
public class AvroWriteDateUtcToEpochMili {
public static void main(String[] args) throws IOException {
Boolean isRegisterNewLogicalType = true;
Boolean isWrite = true;
if(isRegisterNewLogicalType) {
LogicalTypes.register(UtcDateTimeToTimestampMilisLogicalType.CONVERT_LONG_TYPE_NAME, new LogicalTypes.LogicalTypeFactory() {
private final LogicalType convertLongLogicalType = new UtcDateTimeToTimestampMilisLogicalType();
#Override
public LogicalType fromSchema(Schema schema) {
return convertLongLogicalType;
}
});
}
Schema schema1 = new Parser().parse(new File("./userdate_modified_string.avsc"));
// Serialize user1 and user2 to disk
File file1 = new File("users.avro");
if(isWrite) {
GenericRecord user1 = new GenericData.Record(schema1);
user1.put("timestamplong", "2019-07-09T04:31:45.281Z");
//user1.put("timestamplong", 1L);
user1.put("timestampstring", "2019-07-09T04:31:45.281Z");
GenericRecord user2 = new GenericData.Record(schema1);
//user2.put("timestamplong", "2018-07-09T04:30:45.781Z");
user2.put("timestamplong", 2L);
user2.put("timestampstring", (new DateTime(2L)).toString());
//user2.put("timestampstring", new Timestamp(new Date("2018-01-26").getTime()));
var currentDateTime = DateTime.now();
GenericRecord user3 = new GenericData.Record(schema1);
user3.put("timestamplong", currentDateTime.toString());
//user3.put("timestamplong", 3L);
user3.put("timestampstring", currentDateTime.toString());
final GenericData genericData2 = new GenericData();
genericData2.addLogicalTypeConversion(new MyStringTimestampConversion());
DatumWriter<GenericRecord> datumWriter = new GenericDatumWriter<GenericRecord>(schema1, genericData2);
DataFileWriter<GenericRecord> dataFileWriter = new DataFileWriter<GenericRecord>(datumWriter);
dataFileWriter.create(schema1, file1);
dataFileWriter.append(user1);
dataFileWriter.append(user2);
dataFileWriter.append(user3);
dataFileWriter.close();
}
// Deserialize users from disk
Boolean once = true;
DatumReader<GenericRecord> datumReader = new GenericDatumReader<GenericRecord>(schema1);
GenericRecord user = null;
try (DataFileReader<GenericRecord> dataFileReader = new DataFileReader<GenericRecord>(file1, datumReader)) {
while (dataFileReader.hasNext()) {
user = dataFileReader.next(user);
if(once) {
System.out.println(user.getSchema());
once = false;
}
//System.out.println(LogicalTypes.fromSchema(user.getSchema()));
System.out.println(user);
}
}
// Deserialize users from disk
System.out.println("//AFTER");
Schema schema2 = new Parser().parse(new File("./userdate_modified_string.avsc"));
final GenericData genericData = new GenericData();
genericData.addLogicalTypeConversion(new MyStringTimestampConversion());
datumReader = new MyReader<GenericRecord>(schema2, schema2, genericData);
user = null;
try (DataFileReader<GenericRecord> dataFileReader = new DataFileReader<GenericRecord>(file1, datumReader)) {
while (dataFileReader.hasNext()) {
user = dataFileReader.next(user);
System.out.println(user);
}
}
}
public static class MyReader<G extends IndexedRecord> extends GenericDatumReader {
public MyReader() {
super();
}
public MyReader(Schema writer, Schema reader, GenericData data) {
super(writer, reader, data);
}
#Override
protected Object read(Object old, Schema expected, ResolvingDecoder in) throws IOException {
Object datum = this.readWithoutConversion(old, expected, in);
LogicalType logicalType = expected.getLogicalType();
if (logicalType != null) {
Conversion<?> conversion = this.getData().getConversionFor(logicalType);
if (conversion != null) {
return this.convert(datum, expected, logicalType, conversion);
}
}
return datum;
}
}
public static class MyStringTimestampConversion extends Conversion<String> {
public MyStringTimestampConversion() {
super();
}
#Override
public Class<String> getConvertedType() {
return String.class;
}
#Override
public String getLogicalTypeName() {
// "timestamp-millis";
return UtcDateTimeToTimestampMilisLogicalType.CONVERT_LONG_TYPE_NAME;
}
#Override
public String fromLong(Long millisFromEpoch, Schema schema, LogicalType type) {
return (new DateTime(millisFromEpoch)).toString();
//return "123456L";
}
#Override
public Long toLong(String value, Schema schema, LogicalType type) { //https://stackoverflow.com/questions/22681348/joda-datetime-to-unix-datetime
//DateTimeFormatter dtf = DateTimeFormatter.ofPattern("uuuu-MM-dd'T'HH:mm:ss.SSSSSS'Z'");//https://stackoverflow.com/questions/8405087/what-is-this-date-format-2011-08-12t201746-384z
DateTime dateTime = DateTime.parse(value);
long epochMilli = dateTime.toDate().toInstant().toEpochMilli();
return epochMilli;
}
}
}
LogicalType
public class UtcDateTimeToTimestampMilisLogicalType extends LogicalType {
//The key to use as a reference to the type
public static final String CONVERT_LONG_TYPE_NAME = "utc-to-epoch-millis";
public UtcDateTimeToTimestampMilisLogicalType() {
super(CONVERT_LONG_TYPE_NAME);
}
#Override
public void validate(Schema schema) {
super.validate(schema);
if (schema.getType() != Schema.Type.LONG) {
throw new IllegalArgumentException(
"Logical type 'utc-to-epoch-millis' must be backed by bytes");
}
}
}
Schema
{
"namespace": "example.avro.modified.string",
"type": "record",
"name": "UserDate",
"fields": [
{
"name": "timestamplong",
"type":
{
"type": "long",
"logicalType": "utc-to-epoch-millis"
}
},
{
"name": "timestampstring",
"type": "string"
}
]
}
Result
{"type":"record","name":"UserDate","namespace":"example.avro.modified.string","fields":[{"name":"timestamplong","type":{"type":"long","logicalType":"utc-to-epoch-millis"}},{"name":"timestampstring","type":"string"}]}
{"timestamplong": 1562646705281, "timestampstring": "2019-07-09T04:31:45.281Z"}
{"timestamplong": 2, "timestampstring": "1970-01-01T07:30:00.002+07:30"}
{"timestamplong": 1601616694713, "timestampstring": "2020-10-02T13:31:34.713+08:00"}
//AFTER
{"timestamplong": "2019-07-09T12:31:45.281+08:00", "timestampstring": "2019-07-09T04:31:45.281Z"}
{"timestamplong": "1970-01-01T07:30:00.002+07:30", "timestampstring": "1970-01-01T07:30:00.002+07:30"}
{"timestamplong": "2020-10-02T13:31:34.713+08:00", "timestampstring": "2020-10-02T13:31:34.713+08:00"}
Process finished with exit code 0

How to use gson for getstring of json?

I am new to use gson.
I found a lots of tutorial there I can learn of gson but there are using recylerview and model file.
JsonObjectRequest request = new JsonObjectRequest(LoginUrl, new JSONObject(params),
new Response.Listener<JSONObject>() {
#Override
public void onResponse(JSONObject response) {
Log.d(TAG , String.valueOf(response));
try {
String statusObject = response.getString("status");
String msgObject = response.getString("msg");
if (statusObject.equals("200")) {
JSONArray jsonArray = response.getJSONArray("response");
for (int i = 0; i < jsonArray.length(); i++) {
JSONObject managerResponse= jsonArray.getJSONObject(i);
// userIdObject = managerResponse.getString("user_id");
// String nameObject = managerResponse.getString("name");
// String emailObject = managerResponse.getString("email");
// String mobileObject = managerResponse.getString("mobile");
// String postobject = managerResponse.getString("post");
// pojectObject = managerResponse.getString("project");
}
}
} catch (JSONException e) {
e.printStackTrace();
}
}
}, new Response.ErrorListener() {
#Override
public void onErrorResponse(VolleyError error) {
}
});
}
Here I can get data from jsonrequest using volley but unable to do that same process using volley and gson. Is there any way to use gson?
Thank You.
Update
My JSON Response
{
"status": "200",
"msg": "Successfully",
"response": [
{
"user_id": "1",
"name": "HEMANT OJHA",
"email": "hemguna#gmail.com",
"mobile": "9584919991",
"address1": "C92, PALLAWI NAGAR BABADIYA KALAN",
"user": "admin",
"api_token": "admin"
}
]
}
Generating POJO class from JSON
// Considering your response consists of json objects & json array
// Create a POJO class for your response with the link above
{
"keyOne": 1,
"keyTwo": "Some Value",
"someArray": [{
"key": "Value"
},
{
"key": "Value"
}
]
}
import java.util.List;
import com.google.gson.annotations.Expose;
import com.google.gson.annotations.SerializedName;
public class ExampleClass {
#SerializedName("keyOne")
#Expose
private int keyOne;
#SerializedName("keyTwo")
#Expose
private String keyTwo;
#SerializedName("someArray")
#Expose
private List<SomeArray> someArray = null;
public int getKeyOne() {
return keyOne;
}
public void setKeyOne(int keyOne) {
this.keyOne = keyOne;
}
public String getKeyTwo() {
return keyTwo;
}
public void setKeyTwo(String keyTwo) {
this.keyTwo = keyTwo;
}
public List<SomeArray> getSomeArray() {
return someArray;
}
public void setSomeArray(List<SomeArray> someArray) {
this.someArray = someArray;
}
}
// Parsing JSON response with GSON
GsonBuilder gsonBuilder = new GsonBuilder();
Gson gson = gsonBuilder.create();
ExampleClass resultObj = gson.fromJson(jsonObject.toString(), ExampleClass.class);
int keyOneValue = resultObj.getKeyOne() // First JSON Object
// Getting String value
String keyTwoValue = resultObj.getKeyTwo() // Second JSON Object
List<SomeArray> yourJSONArray = resultObj.getSomeArray() // Getting JSON Array contents
// Depending on JSON response that you've updated in your question
GsonBuilder gsonBuilder = new GsonBuilder();
Gson gson = gsonBuilder.create();
ExampleClass resultObj = gson.fromJson(jsonObject.toString(),ExampleClass.class);
String status = resultObj.getStatus();
String msg = resultObj.getMsg();
List<Response> responseList = resultObj.getResponse();
The best way to use for entire app is create a Utils class and use it for conversion.
GsonUtils.java
// This Class is useful for mapping Json into Java Objects and vice versa.
public class GsonUtils {
private static final Gson gson = new Gson();
// This will Convert Java Objects into JSON String...
public static String toGson(Object object) {
return gson.toJson(object);
}
// Gives Java Objects from JSON
public static <T> T fromGson(String json, Class<T> type) {
return gson.fromJson(json, type);
}
public static JsonArray fromGson(String json) {
return new JsonParser().parse(json).getAsJsonArray();
}
}
Now convert any json to and from POJO via,
POJO pojoObj = GsonUtils.toGson(POJO.class);
Try this
JSON response
String str = new Gson().toJson(response)

how to deserialize some specific fields only from Json using Gson?

I have the following JSON string
[
{
"channel": "/bvmt/initValues",
"data": {
"value": {
"instrumentIds": "['TN0007250012','TN0007500010']",
"instruments": "[{'mnemonic':'ADWYA','marche':'ADWYA','phaut':5.82,'open':5.82,'nbrTrans':7,'veille':5.82,'time':'11:14:28','recapChange':0.00,'state':'','variation':'.','ref':5.82,'stateGrp':'S','percentChange':-0.34,'last':5.80,'bestTransaction':[{'value':5.82,'qte':'3','time':'10:00:00'},{'value':5.82,'qte':'5','time':'10:02:26'},{'value':5.82,'qte':'145','time':'10:23:27'},{'value':5.81,'qte':'100','time':'10:23:42'},{'value':5.80,'qte':'1000','time':'10:23:42'},{'value':5.73,'qte':'1','time':'10:31:21'},{'value':5.80,'qte':'100','time':'11:14:28'}],'volume':7857.19,'id':'TN0007250012','bestLimits':[{'quantiteAchat':2600,'timeVente':'11:44:10','ordreAchat':1,'prixAchat':5.76,'quantiteVente':100,'timeAchat':'11:44:10','ordreVente':1,'prixVente':5.90},{'quantiteAchat':50,'timeVente':'11:44:10','ordreAchat':1,'prixAchat':5.74,'quantiteVente':210,'timeAchat':'11:44:10','ordreVente':1,'prixVente':5.95},{'quantiteAchat':250,'timeVente':'11:44:10','ordreAchat':2,'prixAchat':5.75,'quantiteVente':187,'timeAchat':'11:44:10','ordreVente':1,'prixVente':5.94},{'quantiteAchat':189,'timeVente':'11:44:10','ordreAchat':3,'prixAchat':5.73,'quantiteVente':1112,'timeAchat':'11:44:10','ordreVente':1,'prixVente':5.97},{'quantiteAchat':44,'timeVente':'11:44:10','ordreAchat':1,'prixAchat':5.72,'quantiteVente':400,'timeAchat':'11:44:10','ordreVente':1,'prixVente':5.98}],'openStatus':'','cto':0,'valuer':'ADWYA','pbas':5.73,'grp':'S','abrv':'ADWYA','houv':'','qto':0,'seuilBas':5.65,'vto':0,'quantite':1354,'seuilHaut':5.99},{'mnemonic':'WIFAK','marche':'WIFAK','phaut':7.11,'open':7.00,'nbrTrans':2,'veille':7.13,'time':'10:24:15','recapChange':0.00,'state':'','variation':'.','ref':7.13,'stateGrp':'S','percentChange':-0.28,'last':7.11,'bestTransaction':[{'value':7.00,'qte':'99','time':'10:17:00'},{'value':7.11,'qte':'100','time':'10:24:15'}],'volume':1404.00,'id':'TN0007200017','bestLimits':[{'quantiteAchat':100,'timeVente':'11:00:19','ordreAchat':1,'prixAchat':6.80,'quantiteVente':100,'timeAchat':'11:00:19','ordreVente':1,'prixVente':7.09},{'quantiteAchat':0,'timeVente':'11:00:19','ordreAchat':0,'prixAchat':0.00,'quantiteVente':82,'timeAchat':'11:00:19','ordreVente':1,'prixVente':7.11},{'quantiteAchat':0,'timeVente':'11:00:19','ordreAchat':0,'prixAchat':0.00,'quantiteVente':284,'timeAchat':'11:00:19','ordreVente':2,'prixVente':7.10},{'quantiteAchat':0,'timeVente':'11:00:19','ordreAchat':0,'prixAchat':0.00,'quantiteVente':222,'timeAchat':'11:00:19','ordreVente':1,'prixVente':7.12},{'quantiteAchat':0,'timeVente':'11:00:19','ordreAchat':0,'prixAchat':0.00,'quantiteVente':110,'timeAchat':'11:00:19','ordreVente':2,'prixVente':7.13}],'openStatus':'','cto':0,'valuer':'WIFACK INT BANK','pbas':7.00,'grp':'S','abrv':'WIFAK','houv':'','qto':0,'seuilBas':6.92,'vto':0,'quantite':199,'seuilHaut':7.34}]"
},
"action": "initValues",
"infoSession": {
"lastInstrumentOrder": 11672,
"state": 1,
"lastInstrumentTime": "12:03:00",
"tradingTime": "08:04:02",
"tradingDate": "2017-04-24"
}
},
"id": "5"
},
{
"channel": "/bvmt/process",
"successful": true,
"id": "5"
}
]
I'm interested only in the content of the "instruments" field , I want to get only the "mnemonic" and "percentChange" fields and deserialize them into an array of Objects like this
public class Data
{
public List<MyObject> objects;
}
public class MyObject
{
String mnemonic;
Float percentChange;
}
How can I do this using Gson ?
Actually you have dozen ways of doing it. It only depends on how you manage your JSON documents. Let's declare a couple of DTOs first:
final class Data {
final List<MyObject> objects;
Data(final List<MyObject> objects) {
this.objects = objects;
}
}
final class MyObject {
final String mnemonic;
final Float percentChange;
MyObject(final String mnemonic, final Float percentChange) {
this.mnemonic = mnemonic;
this.percentChange = percentChange;
}
}
Here are some ways:
Pure JsonElement trees
The following example uses Java 8 Stream API and Gson JSON trees facilities, and it appears to be the simplest way to me:
private static final Gson gson = new Gson();
static Data testUsingJsonTreesOnly(final Reader reader) {
final List<MyObject> objects = StreamSupport.stream(gson.fromJson(reader, JsonElement.class).getAsJsonArray().spliterator(), false)
.map(JsonElement::getAsJsonObject)
.map(jsonObject -> jsonObject.get("data"))
.filter(Objects::nonNull)
.map(JsonElement::getAsJsonObject)
.map(jsonObject -> jsonObject.get("value"))
.filter(Objects::nonNull)
.map(JsonElement::getAsJsonObject)
.map(jsonObject -> jsonObject.get("instruments"))
.filter(Objects::nonNull)
.map(JsonElement::getAsJsonPrimitive)
.map(JsonPrimitive::getAsString)
.map(json -> gson.fromJson(json, JsonElement.class))
.map(JsonElement::getAsJsonArray)
.flatMap(jsonArray -> StreamSupport.stream(jsonArray.spliterator(), false))
.map(jsonElement -> gson.fromJson(jsonElement, MyObject.class))
.collect(toList());
return new Data(objects);
}
Two-pass mappings
This approach way attempts to extract the values in two passes:
deserialize the "outer" object;
get the inner object string and deserialize it in the second pass.
private static final Gson gson = new Gson();
private static final Type channelViewListType = new TypeToken<List<ChannelView>>() {
}.getType();
private static final Type myObjectListType = new TypeToken<List<MyObject>>() {
}.getType();
static Data testUsingDeserializationWithStrings(final Reader reader) {
final List<MyObject> objects = gson.<List<ChannelView>>fromJson(reader, channelViewListType)
.stream()
.filter(Objects::nonNull)
.map(view -> view.data)
.filter(Objects::nonNull)
.map(view -> view.value)
.filter(Objects::nonNull)
.map(view -> view.instruments)
.map((Function<String, List<MyObject>>) instruments -> gson.fromJson(instruments, myObjectListType))
.flatMap(Collection::stream)
.collect(toList());
return new Data(objects);
}
private static final class ChannelView {
final DataView data = null;
}
private static final class DataView {
final ValueView value = null;
}
private static final class ValueView {
final String instruments = null;
}
One-pass mappings using type adapters
This is, I would say, level #3: you can implement a specific type adapter to "unwrap" the encoded JSON document. #JsonAdapter can be used to specified the field that contains the specific "inner" JSON document:
private static final Gson gson = new Gson();
private static final Type channelViewListType = new TypeToken<List<ChannelView>>() {
}.getType();
static Data testUsingDeserializationWithJsonAdapter(final Reader reader) {
final List<MyObject> objects = gson.<List<ChannelView>>fromJson(reader, channelViewListType)
.stream()
.filter(Objects::nonNull)
.map(view -> view.data)
.filter(Objects::nonNull)
.map(view -> view.value)
.filter(Objects::nonNull)
.map(view -> view.instruments)
.flatMap(Collection::stream)
.collect(toList());
return new Data(objects);
}
private static final class ChannelView {
final DataView data = null;
}
private static final class DataView {
final ValueView value = null;
}
private static final class ValueView {
#JsonAdapter(UnpackedJsonTypeAdapterFactory.class)
final List<MyObject> instruments = null;
}
private static final class UnpackedJsonTypeAdapterFactory
implements TypeAdapterFactory {
#Override
public <T> TypeAdapter<T> create(final Gson gson, final TypeToken<T> typeToken) {
return new UnpackedJsonTypeAdapter<>(gson.getAdapter(typeToken));
}
private static final class UnpackedJsonTypeAdapter<T>
extends TypeAdapter<T> {
private final TypeAdapter<T> typeAdapter;
private UnpackedJsonTypeAdapter(final TypeAdapter<T> typeAdapter) {
this.typeAdapter = typeAdapter;
}
#Override
#SuppressWarnings("resource")
public void write(final JsonWriter out, final T value)
throws IOException {
out.value(typeAdapter.toJson(value));
}
#Override
public T read(final JsonReader in)
throws IOException {
final String json = in.nextString();
final JsonReader lenientIn = new JsonReader(new StringReader(json));
lenientIn.setLenient(true);
return typeAdapter.read(lenientIn);
}
}
}
Pure streaming
Probably the easiest by concept way, but not that easy to implement because of creating a high-level parser that deals with JSON token stream directly from the beginning to the end. Note that no even Gson instances are introduced.
static Data testUsingStreaming(final Reader reader)
throws IOException {
final List<MyObject> myObjects = new ArrayList<>();
final JsonReader jsonReader = new JsonReader(reader);
jsonReader.beginArray();
while ( jsonReader.hasNext() ) {
jsonReader.beginObject();
while ( jsonReader.hasNext() ) {
switch ( jsonReader.nextName() ) {
case "data":
jsonReader.beginObject();
while ( jsonReader.hasNext() ) {
switch ( jsonReader.nextName() ) {
case "value":
jsonReader.beginObject();
while ( jsonReader.hasNext() ) {
switch ( jsonReader.nextName() ) {
case "instruments":
final String instrumentsJson = jsonReader.nextString();
parseInstruments(instrumentsJson, myObjects);
break;
default:
jsonReader.skipValue();
break;
}
}
jsonReader.endObject();
break;
default:
jsonReader.skipValue();
break;
}
}
jsonReader.endObject();
break;
default:
jsonReader.skipValue();
break;
}
}
jsonReader.endObject();
}
jsonReader.endArray();
return new Data(myObjects);
}
private static void parseInstruments(final String instrumentsJson, final Collection<MyObject> myObjects)
throws IOException {
final JsonReader jsonReader = new JsonReader(new StringReader(instrumentsJson));
jsonReader.setLenient(true);
jsonReader.beginArray();
while ( jsonReader.hasNext() ) {
String mnemonic = null;
Float percentChange = null;
jsonReader.beginObject();
while ( jsonReader.hasNext() ) {
final String name = jsonReader.nextName();
switch ( name ) {
case "mnemonic":
mnemonic = jsonReader.nextString();
break;
case "percentChange":
percentChange = (float) jsonReader.nextDouble();
break;
default:
jsonReader.skipValue();
break;
}
}
if ( mnemonic != null && percentChange != null ) {
myObjects.add(new MyObject(mnemonic, percentChange));
}
jsonReader.endObject();
}
jsonReader.endArray();
}
All of the approaches above produce the same output:
ADWYA: -0.34
WIFAK: -0.28

storing JsonElement in arraylist causes error

Gson gson = new Gson();
JsonParser parser = new JsonParser();
JsonElement obj = parser.parse(Tempx.getString("GSON_FEED","")).getAsJsonObject();
for(JsonElement jsx: obj) {
MainPojo cse = gson.fromJson(jsx, MainPojo.class);
TweetList.add(cse);
Log.w("F:", "" + TweetList.get(0).getStatuses().get(0).getScreenName());
}
Trying to store JsonObjects into an ArrayList, however I get an error in the line under obj
for(JsonElement jsx: obj)
saying
foreach not applicable to type 'com.google.gson.JsonElement
How to fix this?
you can easily read JSONArray into ArrayList of type class to which JSONArray is representing. Thank is GSON this entire process can be done in a single line of code. as shown bellow.
ArrayList<MyItem> items2 = (new Gson()).fromJson(result,new TypeToken<ArrayList<MyItem>>() {}.getType());
Lets assume you are given with a JSONArray of type MyItem above line of code will convert JSONArray into the ArrayList of type MyItem.
I have written a sample code, that you may get a better picture.
import java.util.ArrayList;
import com.google.gson.Gson;
import com.google.gson.reflect.TypeToken;
public class MyTest {
public static void main(String[] args) {
// TODO Auto-generated method stub
ArrayList<MyItem> items = new ArrayList<>();
for (int i = 0; i < 5; i++) {
MyItem item = new MyItem();
item.setRate("rate_"+i);
item.setSomeCode("some_"+i);
items.add(item);
}
String result = (new Gson()).toJson(items);
System.out.println(""+result);
ArrayList<MyItem> items2 = (new Gson()).fromJson(result,new TypeToken<ArrayList<MyItem>>() {}.getType());
System.out.println(""+items2.size());
}
}
class MyItem {
private String rate;
private String someCode;
public String getRate() {
return rate;
}
public void setRate(String rate) {
this.rate = rate;
}
public String getSomeCode() {
return someCode;
}
public void setSomeCode(String someCode) {
this.someCode = someCode;
}
}
Output
[
{
"rate": "rate_0",
"someCode": "some_0"
},
{
"rate": "rate_1",
"someCode": "some_1"
},
{
"rate": "rate_2",
"someCode": "some_2"
},
{
"rate": "rate_3",
"someCode": "some_3"
},
{
"rate": "rate_4",
"someCode": "some_4"
}
]
this JSONArray is converted into ArrayList of type MyItem
I have also written some answers on this topic which you may want to check for further information on serialization and de-serialization using GSON library
Example_1
Example_2
Example_3
Example_4

Copy filtered JSON data from one file to another using gson library in java

I want to copy JSON fields from one file to another but only after the field satisfies a particular condition, as for example
{"dataset":
[
{"album_id":1,
"album_type":"Live Performance",
"artist_name":"John Doe",....
}
]
}
I want to copy only those records which have a user given artist_name or any other property, else skip the tuple for copying. I am using the following code to add the filtered records to a JSONObject "wr" which I then write to my output file. But its not giving me the desired results
public static void dumpJSONElement(JsonElement element) {
if (element.isJsonObject()) {
JsonObject obj = element.getAsJsonObject();
java.util.Set<java.util.Map.Entry<String,JsonElement>> entries = obj.entrySet();
java.util.Iterator<java.util.Map.Entry<String,JsonElement>> iter = entries.iterator();
while (iter.hasNext()) {
java.util.Map.Entry<String,JsonElement> entry = iter.next();
if(entry.getKey().equals(filterKey)){
if(! entry.getValue().toString().replace("\"", "").equals(filterValue)){
wr.put(entry.getKey(), entry.getValue());
}
}
else{
wr.put(entry.getKey(), entry.getValue());
}
dumpJSONElement(entry.getValue());
}
} else if (element.isJsonArray()) {
JsonArray array = element.getAsJsonArray();
java.util.Iterator<JsonElement> iter = array.iterator();
while (iter.hasNext()) {
JsonElement entry = iter.next();
dumpJSONElement(entry);
}
} else if (element.isJsonPrimitive()) {
JsonPrimitive value = element.getAsJsonPrimitive();
} else if (element.isJsonNull()) {
} else {
System.out.println("Error. Unknown type of element");
}
}
use code below code to convert your json string to generic java type List<Map<Object, Object>>, use code below.
import java.lang.reflect.Type;
import java.util.List;
import java.util.Map;
import com.google.gson.Gson;
import com.google.gson.reflect.TypeToken;
public class Test {
public static void main(String... args) {
String str = "[{'id':1,'name':'yogesh'},{'id':2,'name':'aarush', 'degree': 'MCA'}]";
Type type = new TypeToken<List<Map<Object, Object>>>() {
}.getType();
List<Map<Object, Object>> list = new Gson().fromJson(str, type);
System.out.println(new Gson().toJson(list));
filterList(list, "name", "yogesh");
System.out.println(new Gson().toJson(list));
}
public static void filterList(List<Map<Object, Object>> list, String key, Object value) {
for (Map<Object, Object> map : list) {
if (map.containsKey(key)) {
if (map.get(key).equals(value)) {
list.remove(map);
}
}
}
}
}
here i filterd name=yogesh record.
output:
[{"id":1.0,"name":"yogesh"},{"id":2.0,"name":"aarush","degree":"MCA"}]
[{"id":2.0,"name":"aarush","degree":"MCA"}]
I had similar issues and I googled, read a lot about this. In conclusion, the best(most efficient) way (with gson) is to write a custom TypeAdapter for your case.
You can test sample code below (it is working as you expected):
public static void answer() {
String jsonAsText = "{\"dataset\":[{\"album_id\":1,\"album_type\":\"Live Performance\",\"artist_name\":\"John Doe\"},{\"album_id\":2,\"album_type\":\"A Dummy Performance\"}]}";
GsonBuilder gsonBuilder = new GsonBuilder();
gsonBuilder.registerTypeAdapter(List.class, new AlbumInfoListTypeAdapter());
Gson gson = gsonBuilder.create();
List<AlbumInfo> dataSet = gson.fromJson(jsonAsText, List.class);
System.out.println(gson.toJson(dataSet));
}
private static class AlbumInfo {
int album_id;
String album_type;
String artist_name;
}
private static class AlbumInfoListTypeAdapter extends
TypeAdapter<List<AlbumInfo>> {
#Override
public List<AlbumInfo> read(com.google.gson.stream.JsonReader in)
throws IOException {
List<AlbumInfo> dataSet = new ArrayList<AlbumInfo>();
in.beginObject();
while (in.hasNext()) {
if ("dataset".equals(in.nextName())) {
in.beginArray();
while (in.hasNext()) {
in.beginObject();
AlbumInfo albumInfo = new AlbumInfo();
while (in.hasNext()) {
String jsonTag = in.nextName();
if ("album_id".equals(jsonTag)) {
albumInfo.album_id = in.nextInt();
} else if ("album_type".equals(jsonTag)) {
albumInfo.album_type = in.nextString();
} else if ("artist_name".equals(jsonTag)) {
albumInfo.artist_name = in.nextString();
}
}
in.endObject();
if (albumInfo.artist_name != null && !"".equals(albumInfo.artist_name.trim())) {
dataSet.add(albumInfo);
} else {
System.out.println("Album info ignored because it has no artist_name value");
}
}
in.endArray();
}
}
in.endObject();
return dataSet;
}
#Override
public void write(com.google.gson.stream.JsonWriter out,
List<AlbumInfo> dataSet) throws IOException {
out.beginObject();
out.name("dataset").beginArray();
for (final AlbumInfo albumInfo : dataSet) {
out.beginObject();
out.name("album_id").value(albumInfo.album_id);
out.name("album_type").value(albumInfo.album_type);
out.name("artist_name").value(albumInfo.artist_name);
out.endObject();
}
out.endArray();
out.endObject();
}
}
You can modify the read and the write methods. Gson has many cool functions. I strongly suggest you to read samples at this link.
Edit:
Incoming json text:
{
"dataset": [
{
"album_id": 1,
"album_type": "Live Performance",
"artist_name": "John Doe"
},
{
"album_id": 2,
"album_type": "A Dummy Performance"
}
]
}
The output at System.out.println at answer method:
[
{
"artist_name": "John Doe",
"album_type": "Live Performance",
"album_id": 1
}
]

Categories