簡體   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