如何使用 Retrofit 解析 json

Posted

技术标签:

【中文标题】如何使用 Retrofit 解析 json【英文标题】:How to parse json using Retrofit 【发布时间】:2015-11-08 09:15:29 【问题描述】:

我对 android 还很陌生,并且正在使用改造和领域。我很难从 url 中获取这个 json 字符串来正确解析。 这是我到目前为止所做的。

我的界面:

package com.example.stephen.traveland.Rest;
import android.database.Observable;
import com.example.stephen.traveland.Models.Country;
import java.util.List;
import retrofit.http.GET;

public interface TravelAnDInterface 
    @GET("/index-updated.json/")
    Observable<List<Country>> getAllCountries();

这个 java 类是我使用接口从 url 中提取字符串的地方。

package com.example.stephen.traveland.Rest;
import android.content.Context;
import com.google.gson.ExclusionStrategy;
import com.google.gson.FieldAttributes;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.squareup.okhttp.OkHttpClient;

import io.realm.RealmObject;
import retrofit.RestAdapter;
import retrofit.android.AndroidLog;
import retrofit.client.OkClient;
import retrofit.converter.GsonConverter;

public class TravelAnD 
    private static final String URL = "http://data.international.gc.ca/travel-voyage/";
    private final static Gson gson = new GsonBuilder()
        .setExclusionStrategies(new ExclusionStrategy() 
            @Override
            public boolean shouldSkipField(FieldAttributes f) 
                return f.getDeclaringClass().equals(RealmObject.class);
            

            @Override
            public boolean shouldSkipClass(Class<?> clazz) 
                return false;
            
        )
        .create();

    private static TravelAnDInterface sTravelAnDSvc;

    public static TravelAnDInterface getCountries() 

        if (sTravelAnDSvc == null) 
            RestAdapter restAdapter = new RestAdapter.Builder()
                    .setEndpoint(URL)
                    .setClient(new OkClient(new OkHttpClient()))
                    .setConverter(new GsonConverter(gson))
                    .setLogLevel(RestAdapter.LogLevel.FULL)
                    .setLog(new AndroidLog("RETROFIT"))
                    .build();

            sTravelAnDSvc = restAdapter.create(TravelAnDInterface.class);
        

        return sTravelAnDSvc;
    

这是我的对象类之一,因此您可以看到我要如何完成。

package com.example.stephen.traveland.Models;

import io.realm.RealmList;
import io.realm.RealmObject;
import io.realm.annotations.PrimaryKey;

public class Country extends RealmObject 

    @PrimaryKey
    private int country_id;
    private String country_iso;
    private String country_in_english;
    private String country_in_french;
    private int advisory_state;
    private RealmList<DatePublished> datePublished;
    private int advisory_warning;
    private int regional_advisory;
    private int content;
    private String recent_update_type;
    private RealmList<English> english;
    private RealmList<French> french;

    public Country()  super(); 

    public Country(int country_id, String country_iso, String     country_in_english, String country_in_french,
               int advisory_state, int advisory_warning, int regional_advisory, int content) 
        this.country_id = country_id;
        this.country_iso = country_iso;
        this.country_in_english = country_in_english;
        this.country_in_french = country_in_french;
        this.advisory_state = advisory_state;
        this.advisory_warning = advisory_warning;
        this.regional_advisory = regional_advisory;
        this.content = content;

    

    public int getAdvisory_state()  return advisory_state; 

    public void setAdvisory_state(int advisory_state)  this.advisory_state = advisory_state; 

    public int getAdvisory_warning()  return advisory_warning; 

    public void setAdvisory_warning(int advisory_warning)  this.advisory_warning = advisory_warning; 

    public int getContent()  return content; 

    public void setContent(int content)  this.content = content; 

    public int getCountry_id()  return country_id; 

    public void setCountry_id(int country_id)  this.country_id = country_id; 

    public String getCountry_in_english()  return country_in_english; 

    public void setCountry_in_english(String country_in_english)  this.country_in_english = country_in_english; 

    public String getCountry_in_french()  return country_in_french; 

    public void setCountry_in_french(String country_in_french)  this.country_in_french = country_in_french; 

    public String getCountry_iso()  return country_iso; 

    public void setCountry_iso(String country_iso) this.country_iso = country_iso; 

    public RealmList<DatePublished> getDatePublished()  return datePublished; 

    public void setDatePublished(RealmList<DatePublished> datePublished)  this.datePublished = datePublished; 

    public RealmList<English> getEnglish()  return english; 

    public void setEnglish(RealmList<English> english) this.english = english;

    public RealmList<French> getFrench()  return french; 

    public void setFrench(RealmList<French> french)  this.french = french; 

    public String getRecent_update_type()  return recent_update_type; 

    public void setRecent_update_type(String recent_update_type)  this.recent_update_type = recent_update_type; 

    public int getRegional_advisory()  return regional_advisory; 

    public void setRegional_advisory(int regional_advisory)  this.regional_advisory = regional_advisory; 

我已经创建了类来存储数据,但是遇到了一个问题,因为字符串本身很奇怪且难以处理。我希望能得到一些指导,说明我做错了什么。任何帮助将不胜感激。

@编辑 不再将 Realm 用于我的任何对象,并且之前的代码已进行了相当多的更改,因为数据太大而无法存储在手机上。对于一台设备来说,信息量太大了。

但是,新问题或相同问题只是在不同的情况下。我的 retofit 调用很好地抓取了数据,就在我将它们放入对象时,当我想从中提取数据时,该对象为空。这是我的代码的一些小sn-ps。

public void gatherData() 

    final ProgressDialog progress = new ProgressDialog(this);
    progress.setTitle("Please wait...");
    progress.setMessage("Loading data...");
    progress.setProgressStyle(ProgressDialog.STYLE_SPINNER);
    progress.setIndeterminate(false);
    progress.show();

    bossModel1 = new BossModel();

    Gson gson = new GsonBuilder()
            .setFieldNamingPolicy(FieldNamingPolicy.LOWER_CASE_WITH_DASHES)
            .create();

    RestAdapter restAdapter = new RestAdapter.Builder()
            .setEndpoint("http://data.international.gc.ca/travel-voyage")
            .setLogLevel(RestAdapter.LogLevel.FULL)
            .build();

    Api service = restAdapter.create(Api.class);

    service.listRepos(new Callback<BossModel>() 
        @Override
        public void success(BossModel bossModel, Response response) 
            bossModel1 = bossModel;

            Log.i("SUCCESS", "" + bossModel1.getData());

            for (Field field : bossModel1.getData().getClass().getDeclaredFields()) 
                if (!Modifier.isStatic(field.getModifiers())) 
                    Log.i("ahlsdkjha: ", field.getName());
                    countryISO.add(field.getName());
                
            

            progress.dismiss();
        

        @Override
        public void failure(RetrofitError error) 

        
    );


public void gatherVPData(final String countryIso) 
    vpModel1 = new VPModel();

    Gson gson = new GsonBuilder()
            .setFieldNamingPolicy(FieldNamingPolicy.LOWER_CASE_WITH_DASHES)
            .create();

    RestAdapter restAdapter = new RestAdapter.Builder()
            .setEndpoint("http://data.international.gc.ca/travel-voyage")
            .setLogLevel(RestAdapter.LogLevel.FULL)
            .build();

    Api service = restAdapter.create(Api.class);

    service.getCountry(countryIso, new Callback<VPModel>() 

        @Override
        public void success(VPModel vpModel, Response response) 
            vpModel1 = vpModel;
            Log.d("SUCCESS2: ", "" + vpModel1.getData());
        

        @Override
        public void failure(RetrofitError error) 

        
    );

API

public interface Api 
    @GET("/index-updated.json")
    void listRepos(Callback<BossModel> callback);

    @GET("/countries/cta-cap-country.json")
    void getCountry(@Path("country") String countryIso, Callback<VPModel> callback);

这里是我尝试用gatherVPData 提取的数据填充新对象列表的地方。由于某种原因数据为空,因此未填充。

public void populateCountryList(int id) 

    for (int i = 0; i < countryISO.size() - 1; i++) 
        gatherVPData(countryISO.get(i));
        if (vpModel1.getData().getAdvisoryState() == id) 
            countries.add(new Country(
                    vpModel1.getData().getCountryIso(),
                    vpModel1.getData().getAdvisoryState(),
                    vpModel1.getData().getHasAdvisoryWarning(),
                    vpModel1.getData().getHasRegionalAdvisory(),
                    vpModel1.getData().getHasContent(),
                    vpModel1.getData().getUpdateMetadata(),
                    vpModel1.getData().getEng(),
                    vpModel1.getData().getFra()
            ));
        
    

【问题讨论】:

您确定Observable 部分吗?它看起来像 Rx,但你导入了 android.database.Observable 第一个问题是尾随/。查看您的日志,您应该会看到 404 错误。第二个问题是您尝试解析的 json 不适合您要询问的对象。它不是一个列表。它是一个对象,包含一个键 data,其中包含您的对象的映射,通过它们的 iso 代码。您将需要另一个至少包含 Map&lt;String, Country&gt; data; 的模型类 将您的 json 放入格式化程序(例如 jsonlint.com)以查看缩进和新行的样子。 那么您将遇到其他问题:您的密钥不匹配。而且它们无法匹配,因为键有-,这是java字段所没有的。您将需要 @SerializedName 注释。那么你的 3 个 RealmList 对象看起来不正确。 最后一点,您似乎从未真正调用过getAllCountries 方法,我认为它在代码的另一部分? 【参考方案1】:

我会这样:

a/ 将getAllCountries 的结果声明为JsonObject

JsonObject getAllCountries();

b/ 调用方法时,转化获取内容:

JsonObject json = <your adapter here>.getAllCountries();
Map<String, Country> countries = gson.fromJson(jsonResponse.getAsJsonObject("data"),
                    new TypeToken<Map<String, Country>>() 
                    .getType());

基本上你得到"data"中的对象,这是一个JsonObject,并将其解析为String>Country的Map。

【讨论】:

以上是关于如何使用 Retrofit 解析 json的主要内容,如果未能解决你的问题,请参考以下文章

如何使用 Retrofit 解析嵌套/多个 Json 对象

如何使用 Retrofit 2.0 (Kotlin) 正确解析嵌套的 JSON 对象?

如何使用Retrofit 2解析动态JSON(+嵌套对象)

当列表的各个项目使用Retrofit和Gson的格式不同时,如何解析json列表?

如何使用 Retrofit 查询某些 JSON 项目?

在 Kotlin 中使用 Moshi 和 Retrofit 解析具有增量对象名称的 JSON