英文:
JSONObject to ArrayList square brackets missing
问题
我正试图从 https://api.ratesapi.io/api/latest
获取汇率数据,然后将其放入自定义的 Currency
类的 ArrayList<Currency>
中:
public class Currency {
private String shortName;
private double rate;
...
}
JSON 数据如下:
{"base":"EUR","rates":{"GBP":0.90033,"HKD":9.1786,"IDR":17304.0,
"ILS":4.0309,"DKK":7.45,"INR":88.765,"CHF":1.0759,"MXN":26.615,
"CZK":26.202,"SGD":1.6236,"THB":36.832,"HRK":7.468,"MYR":4.9604,
"NOK":10.6538,"CNY":8.2325,"BGN":1.9558,"PHP":58.136,"SEK":10.3165,
"PLN":4.4073,"ZAR":20.7655,"CAD":1.5748,"ISK":160.2,"BRL":6.334,
"RON":4.836,"NZD":1.7828,"TRY":8.5853,"JPY":124.96,"RUB":86.9321,
"KRW":1404.99,"USD":1.1843,"HUF":346.23,"AUD":1.6492},"date":"2020-08-06"}
我使用 org.json
成功将数据放入了一个 JSONObject
中:
JSONObject obj = new JSONObject(getJSON("https://api.ratesapi.io/api/latest"));
据我了解,正常的步骤是将 JSONObject
转换为 JSONArray
。但是尝试执行以下代码时:
JSONArray jsonArray = obj.getJSONArray("rates");
会出现错误信息:
Exception in thread "main" org.json.JSONException: JSONObject["rates"]
is not a JSONArray.
我应该如何修复这个错误,或者是否有其他方法将 JSON 转换为 ArrayList?
我怀疑问题在于 JSON 字符串中缺少方括号。
英文:
I am trying to get the rates from https://api.ratesapi.io/api/latest
into an ArrayList<Currency>
of a custom Currency
class:
public class Currency {
private String shortName;
private double rate;
...
}
The JSON looks like:
{"base":"EUR","rates":{"GBP":0.90033,"HKD":9.1786,"IDR":17304.0,
"ILS":4.0309,"DKK":7.45,"INR":88.765,"CHF":1.0759,"MXN":26.615,
"CZK":26.202,"SGD":1.6236,"THB":36.832,"HRK":7.468,"MYR":4.9604,
"NOK":10.6538,"CNY":8.2325,"BGN":1.9558,"PHP":58.136,"SEK":10.3165,
"PLN":4.4073,"ZAR":20.7655,"CAD":1.5748,"ISK":160.2,"BRL":6.334,
"RON":4.836,"NZD":1.7828,"TRY":8.5853,"JPY":124.96,"RUB":86.9321,
"KRW":1404.99,"USD":1.1843,"HUF":346.23,"AUD":1.6492},"date":"2020-08-06"}
Using org.json
I managed to get the data into a JSONObject
:
JSONObject obj = new JSONObject(getJSON("https://api.ratesapi.io/api/latest"));
As far as I understand, the normal procedure is now to convert the JSONObject
into a JSONArray
. However trying:
JSONArray jsonArray = obj.getJSONArray("rates");
fails with the error message:
Exception in thread "main" org.json.JSONException: JSONObject["rates"]
is not a JSONArray.
How do I fix this error or is there another way to make an ArrayList out of the JSON?
I suspect that the problem are missing square brackets in the JSON string.
答案1
得分: 4
如果您查看API返回的JSON,您将得到一个JSON对象:
{"base":"EUR","rates":{"GBP":0.90033,"HKD":9.1786, ... },"date":"2020-08-06"}
您可能想要执行类似以下的操作:
JSONObject obj = new JSONObject(getJSON("https://api.ratesapi.io/api/latest"));
JSONObject rates = obj.getJSONObject("rates");
final Iterator<String> keys = rates.keys();
while (keys.hasNext()) {
final String key = keys.next();
final Currency currency = new Currency(key, rates.getDouble(key));
// 对Currency执行一些操作
}
英文:
If you take a look at the JSON returned by the API, you get a JSON object:
{"base":"EUR","rates":{"GBP":0.90033,"HKD":9.1786, ... },"date":"2020-08-06"}
You probably want to do something like this:
JSONObject obj = new JSONObject(getJSON("https://api.ratesapi.io/api/latest"));
JSONObject rates = obj.getJSONObject("rates");
final Iterator<String> keys = rates.keys();
while (keys.hasNext()) {
final String key = keys.next();
final Currency currency = new Currency(key, rates.getDouble(key));
// do something with the Currency
}
答案2
得分: 2
你可以使用 ObjectMapper 类来将 json
从某个 URL
转换为某种对象。在这种情况下(如果 json
结构始终相同),可以是 Map<String, Object>
。
ObjectMapper mapper = new ObjectMapper();
URL url = new URL("https://api.ratesapi.io/api/latest");
Map<String, Object> map = mapper.readValue(url, Map.class);
System.out.println(map);
// {base=EUR, rates={GBP=0.90373, HKD=9.1585, ... , AUD=1.6403}
然后,您可以获取内部的 rates
映射,并(如果需要的话)使用 java stream api
将其转换为列表:
Map<String, Double> rates = (Map<String, Double>) map.get("rates");
System.out.println(rates); // {GBP=0.90373, HKD=9.1585, ... , AUD=1.6403}
将 Map<String, Object>
转换为 ArrayList<Currency>
:
ArrayList<Currency> list = rates.entrySet().stream()
.map(entry -> new Currency(entry.getKey(), entry.getValue()))
.collect(ArrayList::new, ArrayList::add, ArrayList::addAll);
System.out.println(list); // [GBP=0.90373, HKD=9.1585, ... , AUD=1.6403]
注意: 添加一个具有两个字段 shortName
和 rate
的构造函数;
<br>
注意: 重写 toString
方法如下:shortName + "=" + rate
;
Maven 依赖项:
<dependency>
<groupId>com.fasterxml.jackson.dataformat</groupId>
<artifactId>jackson-dataformat-xml</artifactId>
<version>2.11.2</version>
</dependency>
<sup>另请参阅:«将 JSON 响应格式化为 Java 数组»。</sup>
英文:
You can use ObjectMapper class to convert json
from some URL
to some kind of object. In this case (if json
structure is always the same) it can be Map<String, Object>
.
ObjectMapper mapper = new ObjectMapper();
URL url = new URL("https://api.ratesapi.io/api/latest");
Map<String, Object> map = mapper.readValue(url, Map.class);
System.out.println(map);
// {base=EUR, rates={GBP=0.90373, HKD=9.1585, ... , AUD=1.6403}, date=2020-08-07}
Then you can get inner rates
map, and (if it is needed) convert it to list using java stream api
:
Map<String, Double> rates = (Map<String, Double>) map.get("rates");
System.out.println(rates); // {GBP=0.90373, HKD=9.1585, ... , AUD=1.6403}
Convert Map<String, Object>
to ArrayList<Currency>
:
ArrayList<Currency> list = rates.entrySet().stream()
.map(entry -> new Currency(entry.getKey(), entry.getValue()))
.collect(ArrayList::new, ArrayList::add, ArrayList::addAll);
System.out.println(list); // [GBP=0.90373, HKD=9.1585, ... , AUD=1.6403]
Note: add a constructor with two fields shortName
and rate
;
<br>
Note: override the toString
method as follows: shortName + "=" + rate
;
Maven dependency:
<dependency>
<groupId>com.fasterxml.jackson.dataformat</groupId>
<artifactId>jackson-dataformat-xml</artifactId>
<version>2.11.2</version>
</dependency>
<sup>See also: «Formatting Json Response into an Array Java».</sup>
答案3
得分: 2
"rates"对象不是一个JSONArray,而是一个JSONObject。
所以你需要执行obj.getJSONObject("rates");
,然后使用map方法迭代JSONObject的字段(例如使用keySet()方法)。
英文:
The object "rates
" is not a JSONArray, is a JSONObject.
So you have to do obj.getJSONObject(rates");
then iterate on the fields of the JSONObject using map methods (for examply using keySet() )
答案4
得分: 2
使用Jackson库和Lombok的工作解决方案可能如下所示:
import com.fasterxml.jackson.annotation.JsonFormat;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
import lombok.*;
import java.util.*;
import java.util.stream.Collectors;
public class CcyApiParser {
@Getter
@Setter
@AllArgsConstructor
@NoArgsConstructor
@ToString
public static class Currency {
private String shortName;
private double rate;
}
@Getter
@Setter
public static class RatesApiResponse {
private String base;
private Map<String, Double> rates;
@JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd")
private LocalDate date;
}
public static void main(String[] args) throws IOException {
ObjectMapper mapper = new ObjectMapper()
.registerModule(new JavaTimeModule()); // to parse date
URL apiUrl = new URL("https://api.ratesapi.io/api/latest");
// read proper api response
RatesApiResponse rates = mapper.readValue(apiUrl, RatesApiResponse.class);
// convert inner rates into list of Currency objects
List<Currency> ccys = rates.getRates().entrySet().stream()
.map(e -> new Currency(e.getKey(), e.getValue()))
.collect(Collectors.toList());
ccys.forEach(ccy -> System.out.printf("%s=%s%n", ccy.getShortName(), ccy.getRate()));
}
}
输出
GBP=0.90033
HKD=9.1786
IDR=17304.0
ILS=4.0309
... 等等。
更新
还可以自定义RatesApiResponse的反序列化,并将"rates"字段的映射移到该类中,以立即转换为货币列表。
@Getter
@Setter
public static class RatesApiResponse {
private String base;
@JsonProperty(access = JsonProperty.Access.READ_ONLY)
private List<Currency> ccys;
@JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd")
private LocalDate date;
// 无rates的getter
// 此自定义setter将rates映射为列表
@JsonProperty("rates")
public void setRates(Map<String, Double> rates) {
ccys = rates.entrySet().stream()
.map(e -> new Currency(e.getKey(), e.getValue()))
.collect(Collectors.toList());
}
}
// 在测试方法中的更新
RatesApiResponse rates = mapper.readValue(src, RatesApiResponse.class);
rates.getCcys().forEach(ccy -> System.out.printf("%s=%s%n", ccy.getShortName(), ccy.getRate()));
英文:
A working solution using Jackson library and Lombok may be as follows:
import com.fasterxml.jackson.annotation.JsonFormat;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
import lombok.*;
import java.util.*;
import java.util.stream.Collectors;
public class CcyApiParser {
@Getter
@Setter
@AllArgsConstructor
@NoArgsConstructor
@ToString
public static class Currency {
private String shortName;
private double rate;
}
@Getter
@Setter
public static class RatesApiResponse {
private String base;
private Map<String, Double> rates;
@JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd")
private LocalDate date;
}
public static void main(String[] args) throws IOException {
ObjectMapper mapper = new ObjectMapper()
.registerModule(new JavaTimeModule()); // to parse date
URL apiUrl = new URL("https://api.ratesapi.io/api/latest");
// read proper api response
RatesApiResponse rates = mapper.readValue(apiUrl, RatesApiResponse.class);
// convert inner rates into list of Currency objects
List<Currency> ccys = rates.getRates().entrySet().stream()
.map(e -> new Currency(e.getKey(), e.getValue()))
.collect(Collectors.toList());
ccys.forEach(ccy -> System.out.printf("%s=%s%n", ccy.getShortName(), ccy.getRate()));
}
}
Output
GBP=0.90033
HKD=9.1786
IDR=17304.0
ILS=4.0309
... etc.
Update
It is also possible to customize deserialization of RatesApiResponse and move mapping of "rates"
into this class to convert immediately into list of currencies.
@Getter
@Setter
public static class RatesApiResponse {
private String base;
@JsonProperty(access = JsonProperty.Access.READ_ONLY)
private List<Currency> ccys;
@JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd")
private LocalDate date;
// no getter for rates
// this customized setter for the map of rates converts into a list
@JsonProperty("rates")
public void setRates(Map<String, Double> rates) {
ccys = rates.entrySet().stream()
.map(e -> new Currency(e.getKey(), e.getValue()))
.collect(Collectors.toList());
}
}
// Updates in the test method
RatesApiResponse rates = mapper.readValue(src, RatesApiResponse.class);
rates.getCcys().forEach(ccy -> System.out.printf("%s=%s%n", ccy.getShortName(), ccy.getRate()));
答案5
得分: 1
> 在线程 "main" 中的异常 org.json.JSONException: JSONObject["rates"]
> 不是一个 JSONArray。
你之所以收到这个错误是因为 rates
不是一个数组形式。它只是一个像 base
和 date
一样的元素,但看起来像一个数组。从 JSON 字符串中获取它,就像你从中获取 base
和 date
一样,然后处理它以创建所需的 List<Currency>
。
下面是带有代码注释解释的工作代码:
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.Reader;
import java.net.URL;
import java.net.URLConnection;
import java.nio.charset.Charset;
import java.util.ArrayList;
import java.util.List;
import org.json.JSONException;
import org.json.JSONObject;
class Currency {
private String shortName;
private double rate;
public Currency(String shortName, double rate) {
this.shortName = shortName;
this.rate = rate;
}
@Override
public String toString() {
return shortName + ":" + rate;
}
}
public class Main {
public static JSONObject getJSON(String url) throws IOException, JSONException {
// 创建给定 URL 的 URLConnection
URLConnection connection = new URL(url).openConnection();
// 添加头部以避免 403 Forbidden HTTP 状态代码
connection.addRequestProperty("User-Agent",
"Mozilla/5.0 (Macintosh; Intel Mac OS X 10.14; rv:79.0) Gecko/20100101 Firefox/79.0" + "");
StringBuilder jsonStr = new StringBuilder();
// 从连接获取 InputStream 并读取响应
try (InputStream is = connection.getInputStream();) {
Reader reader = new BufferedReader(new InputStreamReader(is, Charset.forName("UTF-8")));
int ch;
while ((ch = reader.read()) != -1) {
jsonStr.append((char) ch);
}
}
return new JSONObject(jsonStr.toString());
}
public static void main(String[] args) throws IOException, JSONException {
JSONObject jsonObj = getJSON("https://api.ratesapi.io/api/latest");
// 从 jsonObj 获取 rates
String rates = jsonObj.get("rates").toString();
// 从字符串中删除 {, }, 和 "
String[] keyValArr = rates.replaceAll("[\\{\\\"]}", "").split(",");
// 用于保存 Currency 对象的 List 对象
List<Currency> list = new ArrayList<>();
for (String keyVal : keyValArr) {
// 在 ':' 上拆分每个键值对字符串
String[] curRate = keyVal.split(":");
// 将 Currency 对象添加到 List
list.add(new Currency(curRate[0], Double.parseDouble(curRate[1])));
}
// 显示列表
list.forEach(System.out::println);
}
}
输出:
CHF:1.0804
HRK:7.4595
MXN:26.5127
...
...
...
NZD:1.7786
BRL:6.3274
英文:
> Exception in thread "main" org.json.JSONException: JSONObject["rates"]
> is not a JSONArray.
You got this error because rates
is not in the form of an array. It is simply an element like base
and date
but looks like an array. Get it from the JSON string like you get base
and date
from it and then process it to create the required List<Currency>
.
Given below is the working code with the explanation added as comments in the code:
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.Reader;
import java.net.URL;
import java.net.URLConnection;
import java.nio.charset.Charset;
import java.util.ArrayList;
import java.util.List;
import org.json.JSONException;
import org.json.JSONObject;
class Currency {
private String shortName;
private double rate;
public Currency(String shortName, double rate) {
this.shortName = shortName;
this.rate = rate;
}
@Override
public String toString() {
return shortName + ":" + rate;
}
}
public class Main {
public static JSONObject getJSON(String url) throws IOException, JSONException {
// Create a URLConnection for the given URL
URLConnection connection = new URL(url).openConnection();
// Add header to avoid 403 Forbidden HTTP status code
connection.addRequestProperty("User-Agent",
"Mozilla/5.0 (Macintosh; Intel Mac OS X 10.14; rv:79.0) Gecko/20100101 Firefox/79.0" + "");
StringBuilder jsonStr = new StringBuilder();
// Get InputStream from connection and read the response
try (InputStream is = connection.getInputStream();) {
Reader reader = new BufferedReader(new InputStreamReader(is, Charset.forName("UTF-8")));
int ch;
while ((ch = reader.read()) != -1) {
jsonStr.append((char) ch);
}
}
return new JSONObject(jsonStr.toString());
}
public static void main(String[] args) throws IOException, JSONException {
JSONObject jsonObj = getJSON("https://api.ratesapi.io/api/latest");
// Get rates from jsonObj
String rates = jsonObj.get("rates").toString();
// Remove {, }, and " from the string
String[] keyValArr = rates.replaceAll("[\\{\\\"}]", "").split(",");
// List object to hold Currency objects
List<Currency> list = new ArrayList<>();
for (String keyVal : keyValArr) {
// Split each key:value string on ':'
String[] curRate = keyVal.split(":");
// Add Currency object to List
list.add(new Currency(curRate[0], Double.parseDouble(curRate[1])));
}
// Display list
list.forEach(System.out::println);
}
}
Output:
CHF:1.0804
HRK:7.4595
MXN:26.5127
...
...
...
NZD:1.7786
BRL:6.3274
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论