I'm trying to consume the following API result into a Java object.
The code can be seen below.
public Map<String, List<CurrencyData>> gsonCurrency(String answer) {
Gson g = new Gson();
CurrencyData currencyData = null;
Map<String, List<CurrencyData>> object;
try {
object = g.fromJson(answer,
new TypeToken<Map<String, List<CurrencyData>>>().getType());
} catch (Exception e) {
throw new OutputFromApiException("HistoricalFlight API output is empty ", e.toString());
}
return object;
public class CurrencyData {
#SerializedName("rates")
#Expose
private Rates rates;
#SerializedName("base")
#Expose
private String base;
#SerializedName("date")
#Expose
private String date;
// Getters & Setters
}
I get the following error.
Expected BEGIN_ARRAY but was BEGIN_OBJECT at line 1 column 11 path $.
Your CurrencyData is not representative of the payload from the API endpoint you are calling. The quickest fix is to change Rates rate to a Map of String currency keys to BigDecimal's. Don't use Double as you will get precision errors.
public static class CurrencyData {
#SerializedName("rates")
#Expose
private Map<String, BigDecimal> rates;
...
}
You are also deserializing the wrong data structure. You only need to deserialize an instance of CurrencyData.
public CurrencyData gsonCurrency(String answer) {
try {
return new Gson().fromJson(answer, CurrencyData.class);
} catch (Exception e) {
throw new OutputFromApiException("HistoricalFlight API output is empty ", e.toString());
}
}
You need to modify your CurrencyData class to look like the following for it to be able to consume your API result.
public final class CurrencyData {
#SerializedName("rates")
private Map<String, BigDecimal> rates = new HashMap<>();
#SerializedName("base")
private String base;
#SerializedName("date")
private String date;
public Map<String, BigDecimal> getRates() {
return rates;
}
public void setRates(Map<String, BigDecimal> rates) {
this.rates = rates;
}
public String getBase() {
return base;
}
public void setBase(String base) {
this.base = base;
}
public String getDate() {
return date;
}
public void setDate(String date) {
this.date = date;
}
}
Now you can do the following example.
Gson gson = new Gson();
CurrencyData data = new CurrencyData();
data.setBase("USD");
data.setDate("2019-01-01");
Map<String, BigDecimal> ratesMap = new HashMap<>();
ratesMap.put("SEK", new BigDecimal("9.1"));
ratesMap.put("DKK", new BigDecimal("8.2"));
data.setRates(ratesMap);
String json = gson.toJson(data);
System.out.println(json);
Which prints:
{"base":"USD","date":"2019-01-01","rates":{"DKK":8.2,"SEK":9.1}}
You can also reverse the process like so.
CurrencyData parsedData = gson.fromJson(json, CurrencyData.class);
// Prints only the "rates"
System.out.println(parsedData.getRates().toString());
Which prints:
{DKK=8.2, SEK=9.1}
Related
This is my custom object:
public class Proceeding {
#SerializedName("code")
private String code;
#SerializedName("hasAccount")
private boolean hasAccount;
#SerializedName("description")
private String description;
#SerializedName("pricePattern")
private String pricePattern;
// Getters & Setters ...
}
I need a TypeConverter class for Room and this is my TypeConverter class:
public class ProceedingTypeConverter {
#TypeConverter
public static Proceeding fromString(String value) {
Type type = new TypeToken<String>() {
}.getType();
return new Gson().fromJson(value, type);
}
#TypeConverter
public static String fromObject(Proceeding obj) {
Gson gson = new Gson();
return gson.toJson(obj);
}
}
but I'm getting error in return new Gson().fromJson(value, type); line.
Error:
com.google.gson.JsonSyntaxException: java.lang.IllegalStateException:
Expected a string but was BEGIN_OBJECT at line 1 column 2 path $
How can I solve the problem?
UPDATE:
I changed my TypeConverter class to:
public class ProceedingTypeConverter {
#TypeConverter
public Proceeding fromString(String value) {
return new Gson().fromJson(value, Proceeding.class);
}
#TypeConverter
public static String fromObject(Proceeding obj) {
Gson gson = new Gson();
return gson.toJson(obj);
}
}
but I'm still getting error when I Call API:
com.google.gson.JsonSyntaxException: java.lang.IllegalStateException:
Expected a string but was BEGIN_OBJECT at line 1 column 3125 path $[0].store
I have a JSON String as below:
"{ \"password\":\"des123\",\"ROU_DATA\":[{\"FORM_RECEIVING_TIME\":\"12:00:00\",\"REMARKS\":\"Redemption of Unit\"}, {\"FORM_RECEIVING_TIME\":\"13:00:00\",\"REMARKS\":\"sALE of Unit\"}] }";
Now I want to extract the Array from it and need to use it as a separate pojo class so that I can iterate over each value..
Now the problem is, when I try to convert the complete String to Map and get the Array value from the map.. It transforms its format to MAp format like:
{FORM_RECEIVING_DATE = 12:00:00, etc..}
However json string should be {"FORM_RECEIVING_DATE": "12:00:00", etc..}
due to the MAp format its now allowing me to parse it using my POJO Class..
Please help to convert it to my JSONFormat ...
**NOTE: Please note that I can only use Jackson **.
CLASS A
ObjectMapper mapper2 = new ObjectMapper();
Map<String, Object> map;
map = mapper2.readValue(json, new TypeReference<Map<String, Object>>(){});
System.out.println("map: " + map.get("ROU_DATA") );
String array = map.get("ROU_DATA").toString();
String json2 = new ObjectMapper().writeValueAsString(array.replace("[", "").replace("]", ""));
String json3 = new ObjectMapper().writeValueAsString(json2);
System.out.println("json2>>" + json2);
System.out.println("json2>>" + json3);
mapper.configure(JsonParser.Feature.ALLOW_UNQUOTED_FIELD_NAMES, true);
// 1. convert JSON array to Array objects
ROU[] pp1 = mapper.readValue("{" + array.replace("=", ":") + "}", ROU[].class);
for (ROU person : pp1) {
System.out.println(person.getRemarks());
}
CLASS B
import com.fasterxml.jackson.annotation.JsonProperty;
public class ROU {
#JsonProperty("FORM_RECEIVING_TIME")
private String formdate;
#JsonProperty("REMARKS")
private String remarks;
public String getFormdate() {
return formdate;
}
public void setFormdate(String formdate) {
this.formdate = formdate;
}
public String getRemarks() {
return remarks;
}
public void setRemarks(String remarks) {
this.remarks = remarks;
}
}
map.get("ROU_DATA") returns a List object, and the toString() method of List does not generate JSON text.
You don't need to convert back to a JSON text just to get the ROU[] created, just call convertValue(...).
String input = "{ \"password\":\"des123\",\"ROU_DATA\":[{\"FORM_RECEIVING_TIME\":\"12:00:00\",\"REMARKS\":\"Redemption of Unit\"}, {\"FORM_RECEIVING_TIME\":\"13:00:00\",\"REMARKS\":\"sALE of Unit\"}] }";
ObjectMapper mapper2 = new ObjectMapper();
Map<?, ?> json = mapper2.readValue(input, Map.class);
ROU[] pp1 = mapper2.convertValue(json.get("ROU_DATA"), ROU[].class);
for (ROU person : pp1)
System.out.println(person.getRemarks());
Output
Redemption of Unit
sALE of Unit
class A
public class ROU {
#JsonProperty("FORM_RECEIVING_TIME")
private String formdate;
#JsonProperty("REMARKS")
private String remarks;
public String getFormdate() {
return formdate;
}
public void setFormdate(String formdate) {
this.formdate = formdate;
}
public String getRemarks() {
return remarks;
}
public void setRemarks(String remarks) {
this.remarks = remarks;
}
}
class B
public class ObjOuter {
private String password;
#JsonProperty("ROU_DATA")
private List<ROU> rous;
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public List<ROU> getRous() {
return rous;
}
public void setRous(List<ROU> rous) {
this.rous = rous;
}
}
json to Object
ObjectMapper mapper = new ObjectMapper();
try {
ObjOuter outer = mapper.readValue(str, ObjOuter.class);
for (ROU rou : outer.getRous()) {
System.out.println(rou.getFormdate());
System.out.println(rou.getRemarks());
}
} catch (IOException e) {
e.printStackTrace();
}
I need to parse a JSON file that contains long list of customers. In the JSON file each customer may have one id as a string:
{
"cust_id": "87655",
...
},
or a few ids as an array:
{
"cust_id": [
"12345",
"45678"
],
...
},
The Customer class is as below:
public class Customer {
#SerializedName("cust_id")
#Expose
private String custId;
public String getCustId() {
return custId;
}
public void setCustId(String custId) {
this.custId = custId;
}
}
I parse the JSON using Gson:
Gson gson = new Gson()
Customers customers1 = gson.fromJson(json, Customers.class)
and it fails with com.google.gson.JsonSyntaxException: java.lang.IllegalStateException: Expected a string but was BEGIN_ARRAY when it attempts to parse the array.
The reason of failure is clear.
My question: what is the best way to handle both cases (when id is a string and when it is an array of strings), given I can not change the json file structure?
If you want to handle both scenarios you can use a custom deserializer. Of course, you have to change the "cust_id" variable to be a list or an array.
Main:
String json1 = "{\"cust_id\": \"87655\"}";
String json2 = "{\"cust_id\": [\"12345\", \"45678\"]}";
GsonBuilder gsonBuilder = new GsonBuilder();
gsonBuilder.registerTypeAdapter(Customer.class, new CustomerDeserializer());
Gson gson = gsonBuilder.create();
Customer customer1 = gson.fromJson(json1, Customer.class);
System.out.println(customer1);
Customer customer2 = gson.fromJson(json2, Customer.class);
System.out.println(customer2);
Customer
public class Customer {
#SerializedName("cust_id")
private List<String> custId;
public List<String> getCustId() {
return custId;
}
public void setCustId(List<String> custId) {
this.custId = custId;
}
}
CustomerDeserializer
public class CustomerDeserializer implements JsonDeserializer<Customer> {
#Override
public Customer deserialize(JsonElement jsonElement, Type typeOf, JsonDeserializationContext context) throws JsonParseException {
Customer result = null;
Gson gson = new Gson();
try {
// try to deserialize by assuming JSON has a list
result = gson.fromJson(jsonElement, Customer.class);
} catch (JsonSyntaxException jse) {
// error here means JSON has a single string instead of a list
try {
// get the single ID
String custId = jsonElement.getAsJsonObject().get("cust_id").getAsString();
result = new Customer();
result.setCustId(Arrays.asList(new String[] {custId}));
} catch (Exception e) {
// more error handling here
e.printStackTrace();
}
}
return result;
}
}
Output
Customer [custId=[87655]]
Customer [custId=[12345, 45678]]
Try method overriding:
public class Customer {
#SerializedName("cust_id")
#Expose
private String custId;
public void setCustId(String custId) {
this.custId = {custId};
}
public String[] getCustId() {
return custId;
}
#override
public void setCustId(String[] custId) {
this.custId = custId;
}
}
Now In the code all values of CUSTID will be arrays instead of strings
You can just simply specify all values as array, even if is just one value.
{
"cust_id": ["87655"],
...
},
UPDATE: If you cannot change the json, you can bind every field in Customer class except custId and set it manually.
public class Customer {
private String[] custId;
public String getCustId() {
return custId;
}
public void setCustId(String custId) {
custId = new String[] {custId};
}
public void setCustId(String[] custId) {
this.custId = custId;
}
}
And then parse manually:
Gson gson = new Gson();
Customers customers = gson.fromJson(json, Customers.class);
Object custId = new JSONObject(json).get("cust_id");
if (custId instanceof String) {
customers.setCustId((String) custId);
else if (custId instanceof JSONArray) {
customers.setCustId(convertToStringArray(new JSONArray(custId)));
}
Refer this.
Now the problem is that you will have to write your own code on the returned map to get the desired result.
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
}
Say I have a JSON string like:
{"title":"aaa","url":"bbb","image":{"url":"ccc","width":"100","height":"200"}, ...
My accessor:
import com.google.gson.annotations.SerializedName;
public class accessorClass {
#SerializedName("title")
private String title;
#SerializedName("url")
private String url;
#SerializedName("image")
private String image;
// how do I place the sub-arrays for the image here?
...
public final String get_title() {
return this.title;
}
public final String get_url() {
return this.url;
}
public final String get_image() {
return this.image;
}
...
}
And my main:
Gson gson = new Gson();
JsonParser parser = new JsonParser();
JsonArray Jarray = parser.parse(jstring).getAsJsonArray();
ArrayList<accessorClass > aens = new ArrayList<accessorClass >();
for(JsonElement obj : Jarray )
{
accessorClass ens = gson.fromJson( obj , accessorClass .class);
aens.add(ens);
}
What do you think would be the best way to get those sub-arrays for the image here?
FYI if your JSON is an array: {"results:":[{"title":"aaa","url":"bbb","image":{"url":"ccc","width":"100","height":"20...},{}]}
Then you need a wrapper class:
class WebServiceResult {
public List<AccessorClass> results;
}
If your JSON isn't formatted like that, then your For loop you created will do it, (if not a little clunky, would be better if your JSON is formed like above).
Create an Image class
class ImageClass {
private String url;
private int width;
private int height;
// Getters and setters
}
Then change your AccessorClass
#SerializedName("image")
private ImageClass image;
// Getter and setter
Then GSON the incoming String
Gson gson = new Gson();
AccessorClass object = gson.fromJson(result, AccessorClass.class);
Job done.