“`json JSONObject转ArrayList,方括号丢失 “`

huangapple go评论63阅读模式
英文:

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&lt;Currency&gt; of a custom Currency class:

public class Currency {
    private String shortName;
    private double rate;
    ...
}

The JSON looks like:

{&quot;base&quot;:&quot;EUR&quot;,&quot;rates&quot;:{&quot;GBP&quot;:0.90033,&quot;HKD&quot;:9.1786,&quot;IDR&quot;:17304.0,
 &quot;ILS&quot;:4.0309,&quot;DKK&quot;:7.45,&quot;INR&quot;:88.765,&quot;CHF&quot;:1.0759,&quot;MXN&quot;:26.615,
 &quot;CZK&quot;:26.202,&quot;SGD&quot;:1.6236,&quot;THB&quot;:36.832,&quot;HRK&quot;:7.468,&quot;MYR&quot;:4.9604,
 &quot;NOK&quot;:10.6538,&quot;CNY&quot;:8.2325,&quot;BGN&quot;:1.9558,&quot;PHP&quot;:58.136,&quot;SEK&quot;:10.3165,
 &quot;PLN&quot;:4.4073,&quot;ZAR&quot;:20.7655,&quot;CAD&quot;:1.5748,&quot;ISK&quot;:160.2,&quot;BRL&quot;:6.334,
 &quot;RON&quot;:4.836,&quot;NZD&quot;:1.7828,&quot;TRY&quot;:8.5853,&quot;JPY&quot;:124.96,&quot;RUB&quot;:86.9321,
 &quot;KRW&quot;:1404.99,&quot;USD&quot;:1.1843,&quot;HUF&quot;:346.23,&quot;AUD&quot;:1.6492},&quot;date&quot;:&quot;2020-08-06&quot;}

Using org.json I managed to get the data into a JSONObject:

JSONObject obj = new JSONObject(getJSON(&quot;https://api.ratesapi.io/api/latest&quot;));

As far as I understand, the normal procedure is now to convert the JSONObject into a JSONArray. However trying:

JSONArray jsonArray = obj.getJSONArray(&quot;rates&quot;);

fails with the error message:

Exception in thread &quot;main&quot; org.json.JSONException: JSONObject[&quot;rates&quot;]
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:

{&quot;base&quot;:&quot;EUR&quot;,&quot;rates&quot;:{&quot;GBP&quot;:0.90033,&quot;HKD&quot;:9.1786, ... },&quot;date&quot;:&quot;2020-08-06&quot;}

You probably want to do something like this:

JSONObject obj = new JSONObject(getJSON(&quot;https://api.ratesapi.io/api/latest&quot;));
JSONObject rates = obj.getJSONObject(&quot;rates&quot;);
final Iterator&lt;String&gt; 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&lt;String, Object&gt;

ObjectMapper mapper = new ObjectMapper();
URL url = new URL("https://api.ratesapi.io/api/latest");
Map&lt;String, Object&gt; 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&lt;String, Double&gt; rates = (Map&lt;String, Double&gt;) map.get("rates");

System.out.println(rates); // {GBP=0.90373, HKD=9.1585, ... , AUD=1.6403}

Map&lt;String, Object&gt; 转换为 ArrayList&lt;Currency&gt;

ArrayList&lt;Currency&gt; list = rates.entrySet().stream()
    .map(entry -&gt; 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]

注意: 添加一个具有两个字段 shortNamerate 的构造函数;
<br>
注意: 重写 toString 方法如下:shortName + "=" + rate


Maven 依赖项:

&lt;dependency&gt;
    &lt;groupId&gt;com.fasterxml.jackson.dataformat&lt;/groupId&gt;
    &lt;artifactId&gt;jackson-dataformat-xml&lt;/artifactId&gt;
    &lt;version&gt;2.11.2&lt;/version&gt;
&lt;/dependency&gt;

<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&lt;String, Object&gt;.

ObjectMapper mapper = new ObjectMapper();
URL url = new URL(&quot;https://api.ratesapi.io/api/latest&quot;);
Map&lt;String, Object&gt; 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&lt;String, Double&gt; rates = (Map&lt;String, Double&gt;) map.get(&quot;rates&quot;);

System.out.println(rates); // {GBP=0.90373, HKD=9.1585, ... , AUD=1.6403}

Convert Map&lt;String, Object&gt; to ArrayList&lt;Currency&gt;:

ArrayList&lt;Currency&gt; list = rates.entrySet().stream()
    .map(entry -&gt; 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 + &quot;=&quot; + rate;


Maven dependency:

&lt;dependency&gt;
    &lt;groupId&gt;com.fasterxml.jackson.dataformat&lt;/groupId&gt;
    &lt;artifactId&gt;jackson-dataformat-xml&lt;/artifactId&gt;
    &lt;version&gt;2.11.2&lt;/version&gt;
&lt;/dependency&gt;

<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&quot;);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&lt;String, Double&gt; rates;
@JsonFormat(shape = JsonFormat.Shape.STRING, pattern = &quot;yyyy-MM-dd&quot;)
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(&quot;https://api.ratesapi.io/api/latest&quot;);
// read proper api response
RatesApiResponse rates = mapper.readValue(apiUrl, RatesApiResponse.class);
// convert inner rates into list of Currency objects
List&lt;Currency&gt; ccys = rates.getRates().entrySet().stream()
.map(e -&gt; new Currency(e.getKey(), e.getValue()))
.collect(Collectors.toList());
ccys.forEach(ccy -&gt; System.out.printf(&quot;%s=%s%n&quot;, 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 &quot;rates&quot; 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&lt;Currency&gt; ccys;
@JsonFormat(shape = JsonFormat.Shape.STRING, pattern = &quot;yyyy-MM-dd&quot;)
private LocalDate date;
// no getter for rates
// this customized setter for the map of rates converts into a list
@JsonProperty(&quot;rates&quot;)
public void setRates(Map&lt;String, Double&gt; rates) {
ccys = rates.entrySet().stream()
.map(e -&gt; 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 -&gt; System.out.printf(&quot;%s=%s%n&quot;, ccy.getShortName(), ccy.getRate()));

答案5

得分: 1

> 在线程 "main" 中的异常 org.json.JSONException: JSONObject["rates"]
> 不是一个 JSONArray。

你之所以收到这个错误是因为 rates 不是一个数组形式。它只是一个像 basedate 一样的元素,但看起来像一个数组。从 JSON 字符串中获取它,就像你从中获取 basedate 一样,然后处理它以创建所需的 List&lt;Currency&gt;

下面是带有代码注释解释的工作代码:

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&lt;Currency&gt;.

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 + &quot;:&quot; + 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(&quot;User-Agent&quot;,
&quot;Mozilla/5.0 (Macintosh; Intel Mac OS X 10.14; rv:79.0) Gecko/20100101 Firefox/79.0&quot; + &quot;&quot;);
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(&quot;UTF-8&quot;)));
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(&quot;https://api.ratesapi.io/api/latest&quot;);
// Get rates from jsonObj
String rates = jsonObj.get(&quot;rates&quot;).toString();
// Remove {, }, and &quot; from the string
String[] keyValArr = rates.replaceAll(&quot;[\\{\\\&quot;}]&quot;, &quot;&quot;).split(&quot;,&quot;);
// List object to hold Currency objects
List&lt;Currency&gt; list = new ArrayList&lt;&gt;();
for (String keyVal : keyValArr) {
// Split each key:value string on &#39;:&#39;
String[] curRate = keyVal.split(&quot;:&quot;);
// 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

huangapple
  • 本文由 发表于 2020年8月8日 00:14:09
  • 转载请务必保留本文链接:https://go.coder-hub.com/63305573.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定