繁体   English   中英

需要在 spring 引导项目中从 JSON 响应中过滤 null 值

[英]Needed to filter null values from JSON response in spring boot project

公共 class APIService {

private List<CoinDTO> coinList = new ArrayList<>();

private final TrackerConfigProperties trackerConfig;

//Injecting configuration properties into the constructor.
public APIService(TrackerConfigProperties trackerConfig) {
    this.trackerConfig = trackerConfig;
}

public List<CoinDTO> getCoinList() {
    return coinList;
}

public void setCoinList(List<CoinDTO> coinList) {
    this.coinList = coinList;
}

@PostConstruct
@Scheduled(cron = "* * 1 * * *")
public void getAPIData() throws IOException, JSONException{     
    List<CoinDTO> newData = new ArrayList<>();
    OkHttpClient client = new OkHttpClient();
    
    JSONObject obj = null;
    JSONObject data = null;

    //Use request builder to configure api 
    Request request = new Request.Builder()
        .url(trackerConfig.apiUrl())
        .get()
        .addHeader("X-RapidAPI-Key", trackerConfig.apiKey())
        .addHeader("X-RapidAPI-Host", trackerConfig.apiHost())
        .build();
    
    Response response = null;
    
    try {
        response = client.newCall(request).execute();
                    
        String jsonResponse = response.body().string();
        System.out.println(jsonResponse);

        
        obj = new JSONObject(jsonResponse);
        data = obj.getJSONObject("data");
        JSONArray arr = data.getJSONArray("coins");
                    
        System.out.println(arr);

        for(int i=0; i<arr.length(); i++) {
            CoinDTO coinRecords = new CoinDTO();
            coinRecords.setName(arr.getJSONObject(i).getString("name"));
            coinRecords.setSymbol(arr.getJSONObject(i).getString("symbol"));
            coinRecords.setPrice(arr.getJSONObject(i).getFloat("price"));
            coinRecords.setRank(arr.getJSONObject(i).getInt("rank"));
            coinRecords.setChange(arr.getJSONObject(i).getFloat("change"));
            coinRecords.setUrl(arr.getJSONObject(i).getString("iconUrl"));
            coinRecords.setMarketCap(arr.getJSONObject(i).getString("marketCap"));
            newData.add(coinRecords);
        }
        this.coinList = newData;
        
    }catch(IOException e) {
        e.printStackTrace();
    }
    
    // Requesting response.body().string() more than once will cause illegal state exception.   
}

}

来自 crypto API 的 JSON 响应看起来很可疑。 1-60个结果,获取数据没有问题。 但是当我将响应计数增加到 100 个硬币时。 其中一些有 null 个值,这给了我数字格式异常。 如下所示,

Caused by: org.json.JSONException: JSONObject["change"] is not a number.
    at org.json.JSONObject.getFloat(JSONObject.java:655) ~[json-20180130.jar:na]
    at com.vish.trackerapp.service.APIService.getAPIData(APIService.java:86) ~[classes/:na]
    at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[na:na]
    at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:77) ~[na:na]
    at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[na:na]
    at java.base/java.lang.reflect.Method.invoke(Method.java:568) ~[na:na]
    at org.springframework.beans.factory.annotation.InitDestroyAnnotationBeanPostProcessor$LifecycleElement.invoke(InitDestroyAnnotationBeanPostProcessor.java:424) ~[spring-beans-6.0.2.jar:6.0.2]
    at org.springframework.beans.factory.annotation.InitDestroyAnnotationBeanPostProcessor$LifecycleMetadata.invokeInitMethods(InitDestroyAnnotationBeanPostProcessor.java:368) ~[spring-beans-6.0.2.jar:6.0.2]
    at org.springframework.beans.factory.annotation.InitDestroyAnnotationBeanPostProcessor.postProcessBeforeInitialization(InitDestroyAnnotationBeanPostProcessor.java:192) ~[spring-beans-6.0.2.jar:6.0.2]
    ... 37 common frames omitted
Caused by: java.lang.NumberFormatException: For input string: "null"
    at java.base/jdk.internal.math.FloatingDecimal.readJavaFormatString(FloatingDecimal.java:2054) ~[na:na]
    at java.base/jdk.internal.math.FloatingDecimal.parseFloat(FloatingDecimal.java:122) ~[na:na]
    at java.base/java.lang.Float.parseFloat(Float.java:476) ~[na:na]
    at org.json.JSONObject.getFloat(JSONObject.java:653) ~[json-20180130.jar:na]
    ... 45 common frames omitted

Json回复如下,

{
                "uuid": "aQx_vW8s1",
                "symbol": "XEC",
                "name": "eCash",
                "color": null,
                "iconUrl": "https://cdn.coinranking.com/wdqRaUEhn/xec.png",
                "marketCap": "458134192",
                "price": "0.000023888579923343",
                "listedAt": 1634887994,
                "tier": 1,
                "change": "0.07",
                "rank": 73,
                "sparkline": [
                    "0.000023888455899517",
                    "0.000023873825475088",
                    "0.00002383507280166",
                    "0.000023738913764452",
                    "0.000023705304867018",
                    "0.000023643911925818",
                    "0.000023626155269768",
                    "0.000023699523887097",
                    "0.000023745220955252",
                    "0.000023820571142281",
                    "0.000023801617704237",
                    "0.000023816460854558",
                    "0.000023795169626986",
                    "0.000023858079419847",
                    "0.000023892553157129",
                    "0.000023849240281632",
                    "0.000023857584839752",
                    "0.000024048737766589",
                    "0.00002392004515143",
                    "0.000023808725016789",
                    "0.000023751508927767",
                    "0.000023764344487179",
                    "0.000023826878245569",
                    "0.000023885406955056",
                    "0.000023888537567965"
                ],
                "lowVolume": false,
                "coinrankingUrl": "https://coinranking.com/coin/aQx_vW8s1+ecash-xec",
                "24hVolume": "5918517",
                "btcPrice": "1.416302521e-9"
            }

我试过这个想法,但无法弄清楚如何将其包含在我的代码中。

            newData.stream().filter(v -> v != null);

非常感谢任何建议或想法。

在我看来,一个好的解决方案是使用映射器和 DTO 来处理 json,因为在那里您可以使用 @JsonProperty 标记,使 json 字段不是强制性的。

以下是如何使用映射器:

 ObjectMapper om = new ObjectMapper();
        om.configure(SerializationFeature.FAIL_ON_EMPTY_BEANS, false);
        JSONObject battle= new JSONObject(text);
        JSONObject battleObject = battle.getJSONObject(JsonObject);
        try{
            Battle battleDto =  om.readValue(battleObject.toString(), Battle.class);
            return battleDto;
        }catch (JsonProcessingException e ){
            throw new NoStarPlayerException("No starPlayer for this battle ="+battle.toString());
        }

这里是 DTO:

public class Battle {
    @JsonProperty("battleTime")
    String battletime;
    @JsonIgnore()
    String map;
    @JsonProperty("mode")
    String mode;
    @JsonProperty("type")
    String type;
    @JsonProperty("result")
    String result;
    @JsonProperty("duration")
    int duration;
    @JsonProperty("trophyChange")
    int trophyChange;
    @JsonProperty("starPlayer")
    StarPlayer starPlayer;
    @JsonProperty("teams")
    ArrayList<ArrayList<Player>> teams;
    @JsonProperty("players")
    ArrayList<Player>players;
    @JsonProperty("rank")
    int rank;

要解决缺少的 Json 字段,您可以使用 @JsonIgnore(如果您对该字段不感兴趣)或 @JsonProperty(value = "battleTime",required = false) 如果它可能是也可能不是 null。

我希望我能帮助你,问候

您使用的 json.org 库是 JSON 的一个很好的培训工具,但在实际使用中不是很好。 可用的最佳库是JSON-JacksonGson (来自 Google)。 我个人更喜欢 JSON-Jackson。 使用其中之一来解析您的 JSON ,您将轻松解决问题。

此外,对于像这样的简单情况,我在 JSON-Jackson 库上编写了自己的瘦包装器,它允许您序列化和解析 JSON 到/从 java class。这是JsonUtils class 的 Javadoc 您的代码可能如下所示:

   HashMap<String, Object> map;
    try {
        map = JsonUtils.readObjectFromJsonString(jsonStr, Map.class);
    } catch (IOException e) {
        ...
    }

您可以编写自己的 DTO class 而不是 Map,而不是将Map.class替换为您自己的 class 实例。 JsonUtils class 附带由我编写和维护的开源 MgntUtils 库。 该图书馆可以从 Maven Central hereGithub 获得

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM