JSONObject 到 ArrayList 方括号丢失
Posted
技术标签:
【中文标题】JSONObject 到 ArrayList 方括号丢失【英文标题】:JSONObject to ArrayList square brackets missing 【发布时间】:2020-11-28 00:20:30 【问题描述】:我正在尝试将 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 字符串中缺少方括号。
【问题讨论】:
这能回答你的问题吗? simplest way to read json from a URL in java @KaustubhKhare 虽然相关,但该欺骗不会帮助 OP,因为他们要解析的类与 JSON 内容的结构不匹配。 这更接近***.com/q/22687771/1690217 问题是rates
是一个具有与您的Currency
结构匹配的属性 的对象,您需要转换rates
对象到它的属性数组中。
【参考方案1】:
如果您查看 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));
// do something with the Currency
【讨论】:
@user1583209 当您使用货币数据时,我建议不要使用 double 进行计算。而是使用BigDecimal
来避免舍入问题。像new BigDecimal(rates.getString(key))
这样的东西可能会做【参考方案2】:
对象“rates
”不是 JSONArray,是 JSONObject。
所以你必须 obj.getJSONObject(rates");
then 使用 map 方法迭代 JSONObject 的字段(例如使用 keySet() )
【讨论】:
【参考方案3】:使用 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
... etc.
更新
还可以自定义 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;
// 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()));
【讨论】:
【参考方案4】:您可以使用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, date=2020-08-07
然后您可以获取内部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
的构造函数;
注意:重写toString
方法如下:shortName + "=" + rate
;
Maven 依赖:
<dependency>
<groupId>com.fasterxml.jackson.dataformat</groupId>
<artifactId>jackson-dataformat-xml</artifactId>
<version>2.11.2</version>
</dependency>
另见:«Formatting Json Response into an Array Java»。
【讨论】:
【参考方案5】:线程“main”中的异常 org.json.JSONException: JSONObject["rates"] 不是 JSONArray。
您收到此错误是因为rates
不是数组形式。它只是一个像base
和date
这样的元素,但看起来像一个数组。从 JSON 字符串中获取它,就像从中获取 base
和 date
一样,然后对其进行处理以创建所需的 List<Currency>
。
下面给出的是工作代码,并在代码中以 cmets 形式添加了解释:
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);
输出:
CHF:1.0804
HRK:7.4595
MXN:26.5127
...
...
...
NZD:1.7786
BRL:6.3274
【讨论】:
注意:我使用了 Firefox 插件 HTTP Header Live 来获取标题User-Agent
的值。以上是关于JSONObject 到 ArrayList 方括号丢失的主要内容,如果未能解决你的问题,请参考以下文章
从 JSON 检索数据但无法从 JSONobject 访问 Arraylist