Deserializing a JSON array using Jackson - java

I am getting a JSON response from 3rd Party service provider and it has a array of objects in it.
When i am trying to deserialize JSON using Jackson api's. I am getting following exception
com.fasterxml.jackson.databind.JsonMappingException: Can not deserialize instance of JacksonFields out of START_ARRAY token
at [Source: java.io.BufferedReader#1015a9e; line: 5, column: 26]
My JSON response is
{
"flags" : 1074200577,
"results" : {
"id1" : 0,
"id2" : 0,
"fields" : [
{
"id1" : 19202,
"id2" : 19202,
"count" : 0,
"format" : 8,
"type" : "name",
"flags" : 0,
"group" : 1074,
"value" : "1074"
},
{
"id1" : 19218,
"id2" : 19218,
"count" : 0,
"format" : 8,
"type" : "name",
"flags" : 0,
"group" : 1075,
"value" : "1075"
}
]
}
}
And my POJO class looks like this
import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonProperty;
class JacksonFields {
int id1;
int id2;
int count;
int format;
String type;
int flags;
int group;
String value;
public JacksonFields(){
}
#JsonCreator
public JacksonFields(#JsonProperty("id1") int id1,
#JsonProperty("id2") int id2,
#JsonProperty("count") int count,
#JsonProperty("format") int format,
#JsonProperty("type") String type,
#JsonProperty("flags") int flags,
#JsonProperty("group") int group,
#JsonProperty("value") String value){
this.id1 = id1;
this.id2 = id2;
this.count = count;
this.format = format;
this.type = type;
this.flags = flags;
this.group = group;
this.value = value;
}
public void putId1(int id){
this.id1=id;
}
public void putId2(int id){
this.id2=id;
}
public void putCount(int count){
this.count=count;
}
public void putFormat(int format){
this.format=format;
}
public void putType(String type){
this.type=type;
}
public void putFlag(int flag){
this.flags=flag;
}
public void putGroup(int group){
this.group=group;
}
public void putValue(String val){
this.value=val;
}
}
class JacksonResults {
int id1;
int id2;
JacksonFields fields;
#JsonCreator
public JacksonResults(#JsonProperty("id1") int id1,
#JsonProperty("id2") int id2,
#JsonProperty("fields") JacksonFields fields){
this.id1 = id1;
this.id2 = id2;
this.fields = fields;
}
public JacksonResults(){
}
public void putId1(#JsonProperty("id1") int id){
this.id1 = id;
}
public void putId2(#JsonProperty("id2") int id){
this.id2 = id;
}
public void putFields(#JsonProperty("fields") JacksonFields fie){
this.fields = fie;
}
}
public class JacksonJsonObj{
Long flags;
JacksonResults res;
#JsonCreator
public JacksonJsonObj(#JsonProperty("flags") long flags,
#JsonProperty("results") JacksonResults res){
this.flags = flags;
this.res = res;
}
public JacksonJsonObj(){
}
public void putFlags(#JsonProperty("flags") long flag){
this.flags = flag;
}
public void putResults(#JsonProperty("results") JacksonResults res){
this.res=res;
}
}
I am trying to deserialize JSON using following code
ObjectMapper objmapper = new ObjectMapper();
objmapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
JacksonJsonObj jackobj = objmapper.readValue(new BufferedReader(new inputStreamReader(ipStream)), JacksonJsonObj.class);
if i try to do
JacksonJsonObj[] jackobj = objmapper.readValue(new BufferedReader(new inputStreamReader(ipStream)), JacksonJsonObj[].class);
it fails at the BEGIN_OBJECT itself.
How to read and deserialize the JSON wiht Arrays. Should i write my own deserializer?
EDIT
If i work on JSON String rather than stream i am able to get all Java objects back. But for better performance i want Jackson to work on stream
Alternate way
List<JsonFields> JsonFieldsJackson = new ArrayList<JsonFields>();
ObjectMapper mapper = new ObjectMapper();
mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
JsonNode nodes = mapper.readTree(strbuffer.toString());
nodes.elements();
Iterator<JsonNode> iter = nodes.path("results").path("fields").elements();
while(iter.hasNext()){
JsonNode node = iter.next();
JsonFields fie = mapper.readValue(node.toString(),JsonFields.class);
JsonFieldsJackson.add(fie);
}

I am considering that you already have 2 jars i.e.
1. Jackson Core
2. Jackson Mapper
So for Parsing from JSON to Your POJO
ObjectMapper mapper = new ObjectMapper();
JavaType javaType=mapper.getTypeFactory().constructType(JacksonFields.class);
JacksonFields jksnflds = mapper.readValue(jsonString,javaType);
and thats it !.

To deserialize the JSON you should have 3 class like
class Field{
int id1;
int id2;
int count;
int format;
String type;
int flags;
int group;
String value;
}
class Result{
int id1;
int id2;
Field[] fields;
}
class JacksonFields {
String flags;
Result result;
}
Then you can write code like
JacksonFields jackobj = objmapper.readValue(new BufferedReader(new inputStreamReader(ipStream)), JacksonFields.class);
Then it will work.
Note:-I did not provide proper annotation to the classes you can provide those.

Related

How to parse complex nested JSON in java?

I have a complex nested Json
It has a body similar to this:
{
staus: "Success",
id: 1,
data: [{'Movie':'kung fu panda','% viewed': 50.5},{'Movie':'kung fu panda 2','% viewed':1.5}],
metadata: {'filters':['Movie', 'Percentage Viewed'], 'params':{'content':'Comedy', 'type': 'Movie'}}
}
The only field I care about is data, and metadata is usually an even more complex/nested field. I was trying to map this to:
#JsonIgnoreProperties(ignoreUnknown = true)
class ResponseData{
public Data[] data;
#JsonIgnoreProperties(ignoreUnknown = true)
class Data{
public String Movie;
public double viewed;
}
}
I was looking at Jackson as an option and writing my own serializer and use JsonIgnore to discard the metadata but can't get around it.
Any suggestion on how this could be done?
You can use jackson-utils
public class Foo {
public static void main(String... args) {
ResponseData responseData1 = new ResponseData(
1,
"Success",
new ResponseData.Data[] {
new ResponseData.Data("kung fu panda", 50.5),
new ResponseData.Data("kung fu panda 2", 1.5) },
new ResponseData.Metadata(
new HashSet<>(Arrays.asList("Movie", "Percentage Viewed")),
new ResponseData.Metadata.Params("Comedy", "Movie"))
);
String json = JacksonUtils.prettyPrint().writeValue(responseData1);
System.out.println(json);
ResponseData responseData2 = JacksonUtils.readValue(json, ResponseData.class);
}
}
class ResponseData {
private int id;
private String status;
private Data[] data;
private Metadata metadata;
public ResponseData() {
}
public ResponseData(int id, String status, Data[] data, Metadata metadata) {
this.id = id;
this.status = status;
this.data = data;
this.metadata = metadata;
}
public static class Data {
#JsonProperty("Movie")
private String movie;
#JsonProperty("% viewed")
private double viewedPercents;
public Data() {
}
public Data(String movie, double viewedPercents) {
this.movie = movie;
this.viewedPercents = viewedPercents;
}
}
public static class Metadata {
private Set<String> filters;
private Params params;
public Metadata() {
}
public Metadata(Set<String> filters, Params params) {
this.filters = filters;
this.params = params;
}
public static class Params {
private String content;
private String type;
public Params() {
}
public Params(String content, String type) {
this.content = content;
this.type = type;
}
}
}
}
Console output:
{
"id" : 1,
"status" : "Success",
"data" : [ {
"Movie" : "kung fu panda",
"% viewed" : 50.5
}, {
"Movie" : "kung fu panda 2",
"% viewed" : 1.5
} ],
"metadata" : {
"filters" : [ "Movie", "Percentage Viewed" ],
"params" : {
"content" : "Comedy",
"type" : "Movie"
}
}
}
P.S. As an alternative, there is another util gson-utils with the same syntax.

How to parse Dynamic nested JSON with same keys and store in JAVA Class

I'm trying to read a complex payload(Tree structure) to perform PATCHMAPPING(Partial Update). To start with, I'm reading a JSON from the payload and trying to parse and store it's value.
JSON DATA:
{
"data": [{
"name": "name_new",
"description": "new_decsription",
"group_id": 1,
"values": [{
"inner_group_id": 1,
"addclass_id": [1],
"removeclass_id": [2, 3]
},
{
"inner_group_id": 2,
"addclass_id": [1],
"removeclass_id": [2, 3]
},
{
"addclass_id": [1],
"removeclass_id": [2, 3]
}
]
},
{
"group_id": 2,
"values": [{
"inner_group_id": 2,
"addclass_id": [1, 2, 3],
"removeclass_id": []
}]
}
]
}
NOTE: group_id might have inner_group_id and have classes to remove and classes to add. But it can also directly have classes to remove and classes to add.
I'm using following code to parse it and trying to save in a HASHMAP. But it's not parsing properly.Also, I'm unsure if I save in hashmap, how will I tag each inner_group to it's group or class_id's to it's group.
public static List<Map<String, String>> getKeyValue(JSONObject json) throws JSONException {
Iterator<?> keys;
JSONArray data = json.getJSONArray("data");
List<Map<String, String>> listOfMaps = new ArrayList<Map<String, String>>();
HashMap role = new HashMap();
HashMap group = new HashMap();
for (int i = 0, size = data.length(); i < size; i++)
{
JSONObject objectInArray = data.getJSONObject(i);
keys = objectInArray.keys();
while(keys.hasNext())
{
String nextKeys = (String) keys.next();
if (objectInArray.get(nextKeys) instanceof JSONArray)
{
JSONArray jsonarray = objectInArray.getJSONArray(nextKeys);
for(int ii=0; ii<jsonarray.length();ii++)
{
String jsonarrayString = jsonarray.getString(ii).toString();
JSONObject innerJSON = new JSONObject(jsonarrayString);
String[] elementNames = JSONObject.getNames(innerJSON);
for (String elementName : elementNames)
{
String value = innerJSON.getString(elementName);
group.put(elementName, value);
}
}
}
String[] elementNames = JSONObject.getNames(objectInArray);
// System.out.println(elementNames);
// System.out.printf("%d ELEMENTS IN CURRENT OBJECT:\n", elementNames.length);
for (String elementName : elementNames){
String value = objectInArray.getString(elementName);
role.put(elementName, value);
}
listOfMaps.add(role);
listOfMaps.add(group);
}
}
return listOfMaps;
}
I have made 2 JAVA Classes to set those after I can parse:
Group.java
public class Group {
int groupId;
List<InnerGroup> innerGroups;
List<Integer> addClassId;
List<Integer> removeClassId;
// Getters and Setters
}
**InnerGroup.java**
public class InnerGroup {
int innerGroupId;
List<Integer> addClassId;
List<Integer> removeClassId;
// Getters and Setters
}
I have been stuck on this from last 2 days. Any help is highly appreciated. Thanks in advance.
The classes below, use Jackson to deserialize the JSON. The below is provided to give an idea of how to parse the JSON provided and may not necessarily be a complete solution.
Entity Classes
package org.test;
import java.util.List;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonProperty;
#JsonIgnoreProperties(ignoreUnknown = true)
public class GroupContainer {
#JsonProperty(value = "data")
private List<Group> data;
public List<Group> getData() {return data;}
public void setData(List<Group> data) {this.data = data;}
}//class closing
package org.test;
import java.util.List;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonProperty;
#JsonIgnoreProperties(ignoreUnknown = true)
public class Group {
#JsonProperty(value = "group_id")
private int id=-1;
#JsonProperty(value = "name")
private String name;
#JsonProperty(value = "description")
private String description;
#JsonProperty(value = "values")
private List<InnerGroup> values;
public int getId() {return id;}
public void setId(int id) {this.id = id;}
public String getName() {return name;}
public void setName(String name) {this.name = name;}
public String getDescription() {return description;}
public void setDescription(String description) {this.description = description;}
public List<InnerGroup> getValues() {return values;}
public void setValues(List<InnerGroup> values) {this.values = values;}
}//class closing
package org.test;
import java.util.List;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonProperty;
#JsonIgnoreProperties(ignoreUnknown = true)
public class InnerGroup {
#JsonProperty(value = "inner_group_id")
private int id=-1;
#JsonProperty(value = "removeclass_id")
private List<Integer> removeClassIds;
#JsonProperty(value = "addclass_id")
private List<Integer> addClassIds;
public int getId() {return id;}
public void setId(int id) {this.id = id;}
public List<Integer> getRemoveClassIds() {return removeClassIds;}
public void setRemoveClassIds(List<Integer> removeClassIds) {this.removeClassIds = removeClassIds;}
public List<Integer> getAddClassIds() {return addClassIds;}
public void setAddClassIds(List<Integer> addClassIds) {this.addClassIds = addClassIds;}
}//class closing
Parser Class
package org.test;
import java.io.File;
import java.io.FileInputStream;
import com.fasterxml.jackson.databind.ObjectMapper;
public class JsonParseTest {
public static void main(String[] args) throws Exception{
ObjectMapper mapper=new ObjectMapper();
File jsonFile=new File("D:\\etc\\testworkspace\\ExpJavaProject\\etc\\str.json");
try(FileInputStream fis=new FileInputStream(jsonFile)){
GroupContainer container=mapper.readValue(fis, GroupContainer.class);
//Test =================================
System.out.println(container.getData());
for(Group g:container.getData()) {
System.out.println("Group Name --> "+g.getName());
for(InnerGroup ig:g.getValues()) {
System.out.println("\t\tInner Grp. ID --> "+ig.getId());
}//for closing
}//for closing
//END-Test =================================
}catch(Exception e) {e.printStackTrace();}
}//main closing
}//class closing
NOTE: Add the JSON provided above in the original post in a file of choice and change the path accordingly in the parser class to test.

Cannot deserialize instance of `org.json.JSONObject`

I have a basic SpringBoot 2.1.5.RELEASE app. Using Spring Initializer, JPA, embedded Tomcat, Thymeleaf template engine, and package as an executable JAR file with some RestControllers.
In 1 of the controller this is the body I send:
{
"depositHotel": "xxx",
"destinationHotel": "aaa",
"depositHotelAmount": "0.2",
"destinationHotelAmount": "4",
"destinationAddress": [{
"address": "asdf",
"tag": ""
}],
"refundAddress": [{
"address": "pio",
"tag": ""
}]
}
so I create this class to use it as a RequestBody:
public class HotelswitchHotelOrderRequestBody {
public static class Builder {
private String depositHotel;
private String destinationHotel;
private Float depositHotelAmount;
private Float destinationHotelAmount;
private JSONObject destinationAddress;
private JSONObject refundAddress;
public Builder(String depositHotel, String destinationHotel) {
this.depositHotel = depositHotel;
this.destinationHotel = destinationHotel;
}
public Builder withDepositHotelAmount (Float depositHotelAmount) {
this.depositHotelAmount = depositHotelAmount;
return this;
}
public Builder withDestinationHotelAmount (Float destinationHotelAmount) {
this.destinationHotelAmount = destinationHotelAmount;
return this;
}
public Builder toDestinationAddress (JSONObject destinationAddress) {
this.destinationAddress = destinationAddress;
return this;
}
public Builder toRefundAddress (JSONObject refundAddress) {
this.refundAddress = refundAddress;
return this;
}
public HotelswitchHotelOrderRequestBody build(){
HotelswitchHotelOrderRequestBody order = new HotelswitchHotelOrderRequestBody();
order.depositHotel = this.depositHotel;
order.depositHotelAmount = this.depositHotelAmount;
order.destinationAddress = this.destinationAddress;
order.destinationHotel = this.destinationHotel;
order.destinationHotelAmount = this.destinationHotelAmount;
order.refundAddress = this.refundAddress;
return order;
}
}
private String depositHotel;
private String destinationHotel;
private Float depositHotelAmount;
private Float destinationHotelAmount;
private JSONObject destinationAddress;
private JSONObject refundAddress;
private HotelswitchHotelOrderRequestBody () {
//Constructor is now private.
}
public String getDepositHotel() {
return depositHotel;
}
public void setDepositHotel(String depositHotel) {
this.depositHotel = depositHotel;
}
public String getDestinationHotel() {
return destinationHotel;
}
public void setDestinationHotel(String destinationHotel) {
this.destinationHotel = destinationHotel;
}
public Float getDepositHotelAmount() {
return depositHotelAmount;
}
public void setDepositHotelAmount(Float depositHotelAmount) {
this.depositHotelAmount = depositHotelAmount;
}
public Float getDestinationHotelAmount() {
return destinationHotelAmount;
}
public void setDestinationHotelAmount(Float destinationHotelAmount) {
this.destinationHotelAmount = destinationHotelAmount;
}
public JSONObject getDestinationAddress() {
return destinationAddress;
}
public void setDestinationAddress(JSONObject destinationAddress) {
this.destinationAddress = destinationAddress;
}
public JSONObject getRefundAddress() {
return refundAddress;
}
public void setRefundAddress(JSONObject refundAddress) {
this.refundAddress = refundAddress;
}
}
But I have this error when receiving the object:
JSON parse error: out of START_ARRAY token; nested exception is com.fasterxml.jackson.databind.exc.MismatchedInputException: Cannot deserialize instance of `org.json.JSONObject` out of START_ARRAY token
JSONObject's representation in actual JSON is a hash i.e. {...}. In your json data you're providing an array of hashes [{...}] which is not the same. Judging from your domain I don't think it has to be multiple values, so you can just omit [] in your payload and if it does then the fields in your Java class can be defined as JSONArray.
However, I think you should go with defining an Address class and either using
private Address destinationAddress;
private Address refundAddress;
or if it indeed does have to be an object array
private List<Address> destinationAddresses;
private List<Address> refundAddresses;
I had a similar usecase , where I could not define the json to a POJO. Using com.fasterxml.jackson.databind.JsonNode instead of the JSONObject worked.

Best way to get a Java Object from a JSON string using com.jackson.?

Specifically, I am dealing with a JSON string representing an Array containing numbers and other arrays of numbers - these strings are of the form String string = "[0, [1, 2], [3, 4]]". It represents data of the form [ channelID, [price, amount], [price, amount] ... [price, amount]]; The repeating data can be varying lengths.
My end goal is to turn this string into an object of the form ChannelObject { channel: 0, data: [PriceObject { price: 1, amount: 2 }, PriceObject { price: 3, amount: 4 }] }.
Jackson's native objectMapper throws errors while trying to read the value of an un-encapsulated JSON array to a class, and the only solution I have found is by giving the ObjectMapper parser the string { "data": %s }, substituting in the original JSON, and using class with property #JsonProperty ArrayNode data, but I'm afraid this is an inefficient approach as far as performance.
Is writing a deserializer for "naked" JSON arrays the smartest approach? Is there an easier solution I am missing?
Responses are much appreciated.
Additional info:
ObjectMapper doesn't work.
public static void main(String args[]) {
String input = "[17847,[5391.9,0,-1]";
ObjectMapper mapper = new ObjectMapper();
List<ChannelData> list = mapper.readValue(input, ChannelData[].class);
}
public class ChannelData {
private int channel;
private List<Data> data;
public ChannelData(int channel, List<Data> data) {
this.channel = channel;
this.data = data;
}
public int getChannel() {
return channel;
}
public void setChannel(int channel) {
this.channel = channel;
}
public List<Data> getData() {
return data;
}
public void setData(List<Data> data) {
this.data = data;
}
}
public class Data {
private BigDecimal price;
private int count;
private BigDecimal amount;
public Data() {
}
public Data(BigDecimal price, int count, BigDecimal amount) {
this.price = price;
this.count = count;
this.amount = amount;
}
public BigDecimal getPrice() {
return price;
}
public void setPrice(BigDecimal price) {
this.price = price;
}
public int getCount() {
return count;
}
public void setCount(int count) {
this.count = count;
}
public BigDecimal getAmount() {
return amount;
}
public void setAmount(BigDecimal amount) {
this.amount = amount;
}
}
Error:
Exception in thread "main" com.fasterxml.jackson.databind.exc.InvalidDefinitionException: Cannot construct instance of `co.myproject.Application$ChannelData` (no Creators, like default construct, exist): no int/Int-argument constructor/factory method to deserialize from Number value (17847)
at [Source: (String)"[17847,[5391.9,0,-1]]"; line: 1, column: 2] (through reference chain: java.lang.Object[][0])
at com.fasterxml.jackson.databind.exc.InvalidDefinitionException.from(InvalidDefinitionException.java:67)
at com.fasterxml.jackson.databind.DeserializationContext.reportBadDefinition(DeserializationContext.java:1451)
at com.fasterxml.jackson.databind.DeserializationContext.handleMissingInstantiator(DeserializationContext.java:1027)
at com.fasterxml.jackson.databind.deser.ValueInstantiator.createFromInt(ValueInstantiator.java:262)
at com.fasterxml.jackson.databind.deser.std.StdValueInstantiator.createFromInt(StdValueInstantiator.java:356)
at com.fasterxml.jackson.databind.deser.BeanDeserializerBase.deserializeFromNumber(BeanDeserializerBase.java:1324)
at com.fasterxml.jackson.databind.deser.BeanDeserializer._deserializeOther(BeanDeserializer.java:173)
at com.fasterxml.jackson.databind.deser.BeanDeserializer.deserialize(BeanDeserializer.java:161)
at com.fasterxml.jackson.databind.deser.std.ObjectArrayDeserializer.deserialize(ObjectArrayDeserializer.java:195)
at com.fasterxml.jackson.databind.deser.std.ObjectArrayDeserializer.deserialize(ObjectArrayDeserializer.java:21)
at com.fasterxml.jackson.databind.ObjectMapper._readMapAndClose(ObjectMapper.java:4013)
at com.fasterxml.jackson.databind.ObjectMapper.readValue(ObjectMapper.java:3004)
at co.myproject.Application.run(Application.java:27)
at co.myproject.Application.main(Application.java:39)
Process finished with exit code 1
The error message clearly specifies there is no default constructor (which is no argument constructor) in your ChannelData class, create no argument constructor in ChannelData class.
Since you declared public ChannelData(int channel, List<Data> data) ChannelData argument constructor, it is your responsibility to declare no argument constructor
public class ChannelData {
public ChannelData() { // no arg constructor }

Json API Parsing troubles with Java

I'm running into a few issues similar to what others have had in the past with Json parsing in Java. This is the first time I try something like this so any help/tips is extremely useful.
I'm trying to parse in data from this site: https://api.bitcoinaverage.com/exchanges/USD
I have tried numerous ways with both Json and Gson. And have tried looking for help here but to no avail.
Here are the classes that are set up (these were auto generated):
Info.java:
public class Info{
private String display_URL;
private String display_name;
private Rates[] rates;
private String source;
private Number volume_btc;
private Number volume_percent;
public String getDisplay_URL(){
return this.display_URL;
}
public void setDisplay_URL(String display_URL){
this.display_URL = display_URL;
}
public String getDisplay_name(){
return this.display_name;
}
public void setDisplay_name(String display_name){
this.display_name = display_name;
}
public Rates[] getRates(){
return this.rates;
}
public void setRates(Rates[] rates){
this.rates = rates;
}
public String getSource(){
return this.source;
}
public void setSource(String source){
this.source = source;
}
public Number getVolume_btc(){
return this.volume_btc;
}
public void setVolume_btc(Number volume_btc){
this.volume_btc = volume_btc;
}
public Number getVolume_percent(){
return this.volume_percent;
}
public void setVolume_percent(Number volume_percent){
this.volume_percent = volume_percent;
}
}
Rates.java:
public class Rates {
private Number ask;
private Number bid;
private Number last;
public Number getAsk(){
return this.ask;
}
public void setAsk(Number ask){
this.ask = ask;
}
public Number getBid(){
return this.bid;
}
public void setBid(Number bid){
this.bid = bid;
}
public Number getLast(){
return this.last;
}
public void setLast(Number last){
this.last = last;
}
}
MainClass.java:
public class MainClass {
public static void main(String[] args) throws Exception {
Gson gson = new Gson();
String json = readUrl("https://api.bitcoinaverage.com/exchanges/USD");
Info page = gson.fromJson(json, Info.class);
System.out.println(page.getDisplay_name());
}
private static String readUrl(String urlString) throws Exception {
BufferedReader reader = null;
try {
URL url = new URL(urlString);
reader = new BufferedReader(new InputStreamReader(url.openStream()));
StringBuffer buffer = new StringBuffer();
int read;
char[] chars = new char[1024];
while ((read = reader.read(chars)) != -1)
buffer.append(chars, 0, read);
return buffer.toString();
} finally {
if (reader != null)
reader.close();
}
}
}
When I try to call a getter, a null is returned.
How do I go about parsing the data properly, and then being able to call an attribute from which ever object I want? For example, if I want an attribute from "anx_hk" or "bitfinex".
This is the first time me posting something here so I hope I'm following the proper guidelines.
I also plan on passing this over to Android once I get the fell for parsing Json better. Thanks for the help! It'll greatly be appreciated.
I'll be honest with you, that's a pretty lame API response. Here it is
{
"anx_hk": {
"display_URL": "https://anxbtc.com/",
"display_name": "ANXBTC",
"rates": {
"ask": 454.26,
"bid": 444.46,
"last": 443.78
},
"source": "bitcoincharts",
"volume_btc": 11.73,
"volume_percent": 0.02
},
...,
"timestamp": "Fri, 04 Apr 2014 04:30:26 -0000",
...
}
There's no JSON array here, so you can get rid of all your array types. This response is a JSON object, which contains a bunch of JSON objects (which share a format) and a JSON name value pair where the name is timestamp.
The common JSON objects have two fields of type double (that's what type your field should be, not Number)
"volume_btc": 11.73,
"volume_percent": 0.02
, three fields of type String
"display_URL": "https://anxbtc.com/",
"display_name": "ANXBTC",
"source": "bitcoincharts",
and one that is a JSON object that contains three more doubles
"rates": {
"ask": 454.26,
"bid": 444.46,
"last": 443.78
}
The actual issue here is that, I'm assuming, the JSON objects in the root JSON object have names that may change or new ones may be added. This is not a good fit for a POJO. Instead you'd want to use a Map<String, Info>, but Gson can't map to that by default. It is not well suited for such deserialization. You'd have to provide your own TypeAdapter.
Instead, I'm going to suggest you use Jackson.
If we put that all together, we get something like
class ApiResponse {
private Map<String, Info> page = new HashMap<>();
private Date timestamp;
public Map<String, Info> getPage() {
return page;
}
#JsonAnySetter
public void setPage(String name, Info value) {
page.put(name, value);
}
public Date getTimestamp() {
return timestamp;
}
public void setTimestamp(Date timestamp) {
this.timestamp = timestamp;
}
}
class Info {
private String display_URL;
private String display_name;
private Rates rates;
private String source;
private Double volume_btc;
private Double volume_percent;
public String getDisplay_URL() {
return this.display_URL;
}
public void setDisplay_URL(String display_URL) {
this.display_URL = display_URL;
}
public String getDisplay_name() {
return this.display_name;
}
public void setDisplay_name(String display_name) {
this.display_name = display_name;
}
public Rates getRates() {
return this.rates;
}
public void setRates(Rates rates) {
this.rates = rates;
}
public String getSource() {
return this.source;
}
public void setSource(String source) {
this.source = source;
}
public Double getVolume_btc() {
return this.volume_btc;
}
public void setVolume_btc(Double volume_btc) {
this.volume_btc = volume_btc;
}
public Double getVolume_percent() {
return this.volume_percent;
}
public void setVolume_percent(Double volume_percent) {
this.volume_percent = volume_percent;
}
}
class Rates {
private Double ask;
private Double bid;
private Double last;
public Number getAsk() {
return this.ask;
}
public void setAsk(Double ask) {
this.ask = ask;
}
public Double getBid() {
return this.bid;
}
public void setBid(Double bid) {
this.bid = bid;
}
public Double getLast() {
return this.last;
}
public void setLast(Double last) {
this.last = last;
}
}
With deserialization code such as
String json = readUrl("https://api.bitcoinaverage.com/exchanges/USD");
ObjectMapper mapper = new ObjectMapper();
ApiResponse response = mapper.readValue(json, ApiResponse.class);
System.out.println(response);
With appropriate toString() methods (mine were auto-generated with Eclipse), you would get something like
ApiResponse [pages={bitkonan=Info [display_URL=https://bitkonan.com/, display_name=BitKonan, rates=Rates [ask=475.0, bid=438.01, last=437.0], source=api, volume_btc=7.24, volume_percent=0.01], vaultofsatoshi=Info [display_URL=https://vaultofsatoshi.com, display_name=Vault of Satoshi, rates=Rates [ask=460.0, bid=460.0, last=460.0], source=api, volume_btc=11.46, volume_percent=0.02], bitstamp=Info [display_URL=https://bitstamp.net/, display_name=Bitstamp, rates=Rates [ask=439.16, bid=436.34, last=436.34], source=api, volume_btc=22186.29, volume_percent=35.19], ...}, timestamp=Fri Apr 04 01:02:43 EDT 2014]
as output.
The api response contains many objects, but seems that you are trying to read them as a single Info object.
You may try to read the response as a Map<String, Info>, and iterate the entries.
Map<String, Info> hashMap = gson.fromJson(body, HashMap.class);
for (Map.Entry entry : hashMap.entrySet()) {
// your code
}

Categories