如何在Android中使用基于动态SerializedName的gson解析json对象 [英] How to parse json object with gson based on dynamic SerializedName in Android

查看:175
本文介绍了如何在Android中使用基于动态SerializedName的gson解析json对象的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我该如何用Gson解析这个json对象这个url:
http://apis.skplanetx.com/weather/forecast/3hours?appKey=4ce0462a-3884-30ab-ab13-93efb1bc171f&版本= 1& lon = 127.9259& lat = 36.991

 wind:{
wdir3hour:176.00,
wspd3hour:3.10,
wdir4hour:,
wspd4hour:,
wdir1hour 173.00,
wspd1hour:3.60,
wdir2hour:175.00,
wspd2hour:3.40
},

precipitation:{
sinceOntime1hour:0.00,
type1hour:0,
sinceOntime2hour:0.00,
type2hour :0,
sinceOntime3hour:0.00,
type3hour:0,
sinceOntime4hour:,
type4hour:
},

如何为此json对象编写java模型。

  class Wind {
@SerializedName( wdir1hour)
private String wdir1hour;
@SerializedName(wdir2hour)
私人字符串wdir2hour;
@SerializedName(wdir3hour)
私人字符串wdir3hour;
@SerializedName(wdir4hour)
私人字符串wdir4hour;
}

这个json对象有谁更好?

解决方案

通过您提供的链接访问的当前JSON响应似乎有一些设计问题或怀疑。我会在这里发布JSON,以免在将来丢失它:

  {
weather :{
forecast3hours:[
{
grid:{
city:충북,
county:충주시,
village:목행동,
纬度:37.0135600000,
经度:127.9036500000
},
lightning1hour: 0,
timeRelease:2017-02-24 16:30:00,
wind:{
wspd2hour:3.10,
wdir1hour:179.00,
wspd1hour:4.20,
wdir2hour:176.00,
wdir3hour:,
wspd3hour: ,
wdir4hour:,
wspd4hour:
},
沉淀:{b $ bsinceOntime1hour:0.00,
type1hour:0,
sinceOntime2hour:0.00,
type2hour: 0,
sinceOntime3hour:,
type3hour:,
sinceOntime4hour:,
type4hour:
},
sky:{
code1hour:SKY_V01,
name1hour:맑음,
code2hour:SKY_V01,
name2hour:맑음,
code3hour:,
name3hour:,
code4hour:,
name4hour:

temperature:{
temp1hour:3.20,
temp2hour:2.00,
temp3hour:,
temp4hour:
},
湿度:{
rh1hour:41.00,
rh2hour :50.00,
rh3hour:,
rh4hour:
},
lightning2hour:0,
lightning3hour :,
lightning4hour:
}
]
},
common:{
alertYn:N ,
stormYn:N
},
result:{
code:9200,
requestUrl:/ weather /预测/ 3小时?lon = 127.9259& lat = 36.991& version = 1和appKey = 4ce0462a-3884-30ab-ab13-93efb1bc171f,
消息:结果
}
}

从我的角度来看,它们是:


  • 没有数组,而是人为索引的对象键(这是你questi的主题)

  • 可能为 null 值而不是 null的空字符串 s或者只是排除在响应之外。
  • 几乎所有值都表示为字符串文本,即使它们看起来是非字符串也是如此。

  • 可能是布尔值似乎用 Yn 后缀标记,并定义 true false 分别使用YN
>

这就是为什么自动POJO生成器可能不是处理它的最好方法,因为它们可能不会检测到真实类型的特定JSON字符串值,而且它们也不能生成定制解串器。不知道为什么它是这样设计的,但是你可以设计你的自定义映射,使它更具编程友好性,并且可以更好地控制它们。

  final class Response {

final天气预报= null;
final普通common = null;
final结果结果= null;

@Override
public String toString(){
return new StringBuilder(Response {)
.append(weather =)。append(weather)
.append(,common =)。append(common)
.append(,result =)。append(result)
.append('}')。toString( );
}

}

final class Weather {

final List< Forecast> forecast3hours = null;

@Override
public String toString(){
return new StringBuilder(Weather {)
.append(forecast3hours =)。append(forecast3hours)
.append('}')。toString();
}

}

最终课程预测{

最终电网网格;
最终日期timeRelease;
最终列表<整数>闪电;
最终列表< Wind>风;
final List< Precipitation>沉淀;
最终列表< Sky>天空;
final List< Float>温度;
final List< Float>湿度;

预测(最终网格,最终日期时间发布,最终列表<整数>闪电,最终列表<风>风,最终列表<降水>降水,
final List< Sky> skies,最终List< Float>温度,final List< Float>湿度){
this.grid = grid;
this.timeRelease = timeRelease;
this.lightnings =闪电;
this.winds = winds;
this.precipitations = precipitations;
this.skies =天空;
this.temperatures =温度;
this.humidities =湿度;

$ b @Override
public String toString(){
return new StringBuilder(Forecast {)
.append(grid =) .append(grid)
.append(,timeRelease =)。append(timeRelease)
.append(,lightnings =)。append(lightnings)
.append(, (,),附加(风)
.append(,precipitations =)。附加(降水)
.append(,skies =)。 append(,humidity =)。append(temperatures)
.append(,humidities =)。append(humidities)
.append('}')。toString();
}

}

final class Grid {

final String city = null;
final String county = null;
final String village = null;
final double latitude = Double.valueOf(0); //禁用内联基元double 0
final double longitude = Double.valueOf(0); //禁用内联原语double 0

@Override
public String toString(){
return new StringBuilder(Grid {)
.append(city =')。append(city).append('\'')
.append(,county =')。append(county).append('\'')
.append(,village =')。append(village).append('\'')
.append(,latitude =)。append(latitude)
.append( ,longitude =)。append(longitude)
.append('}')。toString();
}

}

最后一班风{

最终浮动速度;
最终浮动方向;

风(最终浮动速度,最终浮动方向){
this.speed =速度;
this.direction =方向;
}

@Override
public String toString(){
return new StringBuilder(Wind {)
.append(speed =) .append(speed)
.append(,direction =)。append(direction)
.append('}')。toString();
}

}

最终课程降水量{

自开始以来的最后一次浮动;
final int类型;

降水量(最终浮点数,因为最终的int类型){
this.sinceOntime = sinceOntime;
this.type = type;

$ b @Override
public String toString(){
return new StringBuilder(Precipitation {)
.append(sinceOntime =' ).append(sinceOntime).append('\'')
.append(,type =)。append(type)
.append('}')。toString();
}

}

final class Sky {

final String code;
最终字符串名称;

Sky(最终字符串代码,最终字符串名称){
this.code = code;
this.name = name;
}

@Override
public String toString(){
return new StringBuilder(Sky {)
.append(code =' ).append(code).append('\'')
.append(,name =')。append(name).append('\'')
.append( '}')的toString();
}



最后一类常见{

@SerializedName(alertYn)
@JsonAdapter(YnToBooleanJsonDeserializer。类)
最终布尔值alert = Boolean.valueOf(false); //禁止内联原始布尔值false

@SerializedName(stormYn)
@JsonAdapter(YnToBooleanJsonDeserializer.class)
最终布尔值storm = Boolean.valueOf(false); //禁用内联原生布尔值false

@Override
public String toString(){
return new StringBuilder(Common {)
.append(alert =)。append(alert)
.append(,storm =)。append(storm)
.append('}')。toString();
}

}

final class结果{

final int code = Integer.valueOf(0); //禁用内联基元int 0
final String requestUrl = null;
final String message = null;

@Override
public String toString(){
return new StringBuilder(Result {)
.append(code =)。append(code)
.append(,requestUrl =')。append(requestUrl).append('\'')
.append(,message =')。append(message).append( '\'')
.append('}')。toString();
}

}

其中一些映射具有显式构造函数 - 这些对象必须在自定义的反序列化器中手动实例化。如果没有提供构造函数,那么Gson可以自己处理这样的映射,只需要有足够的信息就可以解释一个特定的对象应该如何反序列化。

由于这些数据应该在一个非标准的方式,可以实现一些自定义的反序列化器。以下类型的适配器将YN转换为 true false
$ b

  final类YnToBooleanJsonDeserializer 
实现JsonDeserializer< Boolean> {

// Gson将实例化这个适配器本身
private YnToBooleanJsonDeserializer(){
}

@Override
public Boolean deserialize(final JsonElement jsonElement,final类型类型,最终JsonDeserializationContext上下文)
抛出JsonParseException {
final String rawFlag = jsonElement.getAsString();
switch(rawFlag){
caseN:
return false;
caseY:
return true;
默认值:
抛出新的JsonParseException(Can not parse:+ rawFlag);
}
}

}

下一个 JsonDeserializer 尝试使用正则表达式检测 xxx 小时类似的键并提取 < N> 索引构建构建 Forecast 实例所需的列表。请注意,它可以解析任意大小的列表(JSON中的)。


$ b

  final类ForecastJsonDeserializer 
实现JsonDeserializer<预测> {

//这个反序列化程序不保存任何状态,并且可以在每个应用程序生命周期中实例化一次。
private static final JsonDeserializer< Forecast> forecastJsonDeserializer = new ForecastJsonDeserializer();

私人ForecastJsonDeserializer(){
}

静态JsonDeserializer<预测> getForecastJsonDeserializer(){
返回forecastJsonDeserializer;

$ b @Override
public预测反序列化(最终的JsonElement jsonElement,final类型类型,最终的JsonDeserializationContext上下文)
throws JsonParseException {
final JsonObject jsonObject = jsonElement.getAsJsonObject();
返回新的预测(
context.deserialize(jsonObject.get(grid),Grid.class),
context.deserialize(jsonObject.get(timeRelease),Date.class ),
lightningsExtractor.parseList(jsonObject),
windsExtractor.parseList(jsonObject.get(wind)。getAsJsonObject()),
precipitationsExtractor.parseList(jsonObject.get(precipitation ).getAsJsonObject()),
skiesExtractor.parseList(jsonObject.get(sky)。getAsJsonObject()),
temperaturesExtractor.parseList(jsonObject.get(temperature)。getAsJsonObject()) ,
humiditiesExtractor.parseList(jsonObject.get(humidity)。getAsJsonObject())
);
}

private static final AbstractExtractor< Integer> lightningsExtractor = new AbstractExtractor< Integer>(compile(lightning(\\\d)hour)){
@Override
protected Integer parse(final int index,final JsonObject jsonObject){
final String rawLightning = jsonObject.get(lightning+ index +hour)。getAsString();
if(rawLightning.isEmpty()){
return null;
}
return parseInt(rawLightning);
}
};

private static final AbstractExtractor< Wind> windsExtractor = new AbstractExtractor< Wind>(compile((?:wdir | wspd)(\\d)hour)){
@Override
protected Wind parse(final int index,final JsonObject jsonObject){
String rawSpeed = jsonObject.get(wspd+ index +hour)。getAsString();
String rawDirection = jsonObject.get(wdir+ index +hour)。getAsString();
if(rawSpeed.isEmpty()&& rawDirection.isEmpty()){
return null;
}
返回新的Wind(parseFloat(rawSpeed),parseFloat(rawDirection));
}
};

private static final AbstractExtractor< Precipitation> precipitationsExtractor = new AbstractExtractor< Precipitation>(compile((?:sinceOntime | type)(\\d)hour)){
@Override
protected Precipitation parse(final int index,final JsonObject jsonObject){
final String rawSinceOntime = jsonObject.get(sinceOntime+ index +hour)。getAsString();
final String rawType = jsonObject.get(type+ index +hour)。getAsString();
if(rawSinceOntime.isEmpty()&& rawType.isEmpty()){
return null;
}
返回新的降水量(parseFloat(rawSinceOntime),parseInt(rawType));
}
};

private static final AbstractExtractor< Sky> skiesExtractor = new AbstractExtractor< Sky>(compile((?:code | name)(\\d)hour)){
@Override
保护天空解析(final int index,final JsonObject jsonObject){
final String rawCode = jsonObject.get(code+ index +hour)。getAsString();
final String rawName = jsonObject.get(name+ index +hour)。getAsString();
if(rawCode.isEmpty()&& rawName.isEmpty()){
return null;
}
返回新的Sky(rawCode,rawName);
}
};

private static final AbstractExtractor< Float> temperatureExtractor = new AbstractExtractor< Float>(compile(temp(\\\d)hour)){
@Override
protected Float parse(final int index,final JsonObject jsonObject){
final String rawTemperature = jsonObject.get(temp+ index +hour)。getAsString();
if(rawTemperature.isEmpty()){
return null;
}
return parseFloat(rawTemperature);
}
};

private static final AbstractExtractor< Float>如果你使用的是一个浮点数据库,那么你可以使用下面的方法来创建浮点数据类型:floatitiesExtractor = new AbstractExtractor< Float>(compile(rh(\\\d)hour)){
@Override
protected Float parse(final int index,final JsonObject jsonObject){
final String rawHumidity = jsonObject.get(rh+ index +hour)。getAsString();
if(rawHumidity.isEmpty()){
return null;
}
return parseFloat(rawHumidity);
}
};

私有抽象静态类AbstractExtractor< T> {

私人最终模式模式;

private AbstractExtractor(最终模式模式){
this.pattern = pattern;


protected abstract T parse(int index,JsonObject jsonObject);

私人列表< T> parseList(final JsonObject jsonObject){
final List< T> list = new ArrayList<>();
for(final Entry< String,JsonElement> e:jsonObject.entrySet()){
final String key = e.getKey();
最后匹配匹配器= pattern.matcher(key);
//检查给定的正则表达式是否与键
匹配if(matcher.matches()){
//如果是,那么只需提取并解析索引
final int index = parseInt(matcher.group(1));
//检查结果列表中是否有足够空间,因为JSON响应可能包含无序键
while(index> list.size()){
list.add(null );
}
//由于Java列表是基于0的
if(list.get(index - 1)== null){
//假设null标记一个对象可能还没有被解析
list.set(index - 1,parse(index,jsonObject));
}
}
}
返回列表;
}

}

}



<现在可以将它们放在一起:

  public static void main(final String。 ... args){
final Gson gson = new GsonBuilder()
.setDateFormat(yyyy-MM-dd hh:mm:ss)
.registerTypeAdapter(Forecast.class,getForecastJsonDeserializer ))
.create();
final Response response = gson.fromJson(JSON,Response.class);
System.out.println(response);





输出:

< blockquote>

响应{weather = Weather {forecast3hours = [Forecast {grid = Grid {city ='충북',county ='충주시',village ='목행동',纬度= 37.01356,经度= 127.90365} ,timeRelease = Fri Feb 24 16:30:00 EET 2017,lightnings = [0,0,null,null],winds = [Wind {speed = 4.2,direction = 179.0},Wind {speed = 3.1,direction = 176.0} ,降水量= [天空{code ='SKY_V01',null,null],降水量= [降水量{sinceOntime ='0.0',type = 0},降水量{sinceOntime ='0.0',type = 0},null,null] ,name ='맑음'},Sky {code ='SKY_V01',name ='맑음'},null,null],温度= [3.2,2.0,null,null],湿度= [41.0,50.0,null,null ]}],common = Common {alert = false,storm = false},result = Result {code = 9200,requestUrl ='/ weather / forecast / 3hours?lon = 127.9259& lat = 36.991& version = 1& appKey = 4ce0462a-3884-30ab-ab13-93efb1bc171f',message ='성공'}}



How can I parse this json objects with Gson this url: http://apis.skplanetx.com/weather/forecast/3hours?appKey=4ce0462a-3884-30ab-ab13-93efb1bc171f&version=1&lon=127.9259&lat=36.991

    "wind": {
      "wdir3hour": "176.00",
      "wspd3hour": "3.10",
      "wdir4hour": "",
      "wspd4hour": "",
      "wdir1hour": "173.00",
      "wspd1hour": "3.60",
      "wdir2hour": "175.00",
      "wspd2hour": "3.40"
    },

    "precipitation": {
      "sinceOntime1hour": "0.00",
      "type1hour": "0",
      "sinceOntime2hour": "0.00",
      "type2hour": "0",
      "sinceOntime3hour": "0.00",
      "type3hour": "0",
      "sinceOntime4hour": "",
      "type4hour": ""
    },

How can i write java models for this json objects. I dont want to write varibles each filed like this.

 class Wind{
 @SerializedName("wdir1hour")
 private String wdir1hour;  
 @SerializedName("wdir2hour")
 private String wdir2hour; 
 @SerializedName("wdir3hour")
 private String wdir3hour; 
 @SerializedName("wdir4hour")
 private String wdir4hour; 
 }

Who has better idea for this json objects ??

解决方案

The current JSON response accessible via the link you provided seems to have a few design issues or suspicions for them. I'll post the JSON here in order not to let it be lost in the future:

{
    "weather": {
        "forecast3hours": [
            {
                "grid": {
                    "city": "충북",
                    "county": "충주시",
                    "village": "목행동",
                    "latitude": "37.0135600000",
                    "longitude": "127.9036500000"
                },
                "lightning1hour": "0",
                "timeRelease": "2017-02-24 16:30:00",
                "wind": {
                    "wspd2hour": "3.10",
                    "wdir1hour": "179.00",
                    "wspd1hour": "4.20",
                    "wdir2hour": "176.00",
                    "wdir3hour": "",
                    "wspd3hour": "",
                    "wdir4hour": "",
                    "wspd4hour": ""
                },
                "precipitation": {
                    "sinceOntime1hour": "0.00",
                    "type1hour": "0",
                    "sinceOntime2hour": "0.00",
                    "type2hour": "0",
                    "sinceOntime3hour": "",
                    "type3hour": "",
                    "sinceOntime4hour": "",
                    "type4hour": ""
                },
                "sky": {
                    "code1hour": "SKY_V01",
                    "name1hour": "맑음",
                    "code2hour": "SKY_V01",
                    "name2hour": "맑음",
                    "code3hour": "",
                    "name3hour": "",
                    "code4hour": "",
                    "name4hour": ""
                },
                "temperature": {
                    "temp1hour": "3.20",
                    "temp2hour": "2.00",
                    "temp3hour": "",
                    "temp4hour": ""
                },
                "humidity": {
                    "rh1hour": "41.00",
                    "rh2hour": "50.00",
                    "rh3hour": "",
                    "rh4hour": ""
                },
                "lightning2hour": "0",
                "lightning3hour": "",
                "lightning4hour": ""
            }
        ]
    },
    "common": {
        "alertYn": "N",
        "stormYn": "N"
    },
    "result": {
        "code": 9200,
        "requestUrl": "/weather/forecast/3hours?lon=127.9259&lat=36.991&version=1&appKey=4ce0462a-3884-30ab-ab13-93efb1bc171f",
        "message": "성공"
    }
}

From my point of view they are:

  • No arrays but artificially indexed object keys (and that's the subject of your question).
  • Empty strings for probably null values rather than nulls or just exclusion from the response.
  • Almost all values are expressed as string literals even if they seem to be non-strings.
  • Probably boolean values seem to be marked with the Yn suffix, and define true and false using "Y" and "N" respectively.

This is why automatic POJO generators may be not the best way to deal with it because they may do not detect "real" type of a particular JSON string value, and moreover they cannot generate custom deserializers. Not sure why it was designed that way, but you can design your custom mappings to make it a little more programmatically-friendly with more control over them.

final class Response {

    final Weather weather = null;
    final Common common = null;
    final Result result = null;

    @Override
    public String toString() {
        return new StringBuilder("Response{")
                .append("weather=").append(weather)
                .append(", common=").append(common)
                .append(", result=").append(result)
                .append('}').toString();
    }

}

final class Weather {

    final List<Forecast> forecast3hours = null;

    @Override
    public String toString() {
        return new StringBuilder("Weather{")
                .append("forecast3hours=").append(forecast3hours)
                .append('}').toString();
    }

}

final class Forecast {

    final Grid grid;
    final Date timeRelease;
    final List<Integer> lightnings;
    final List<Wind> winds;
    final List<Precipitation> precipitations;
    final List<Sky> skies;
    final List<Float> temperatures;
    final List<Float> humidities;

    Forecast(final Grid grid, final Date timeRelease, final List<Integer> lightnings, final List<Wind> winds, final List<Precipitation> precipitations,
            final List<Sky> skies, final List<Float> temperatures, final List<Float> humidities) {
        this.grid = grid;
        this.timeRelease = timeRelease;
        this.lightnings = lightnings;
        this.winds = winds;
        this.precipitations = precipitations;
        this.skies = skies;
        this.temperatures = temperatures;
        this.humidities = humidities;
    }

    @Override
    public String toString() {
        return new StringBuilder("Forecast{")
                .append("grid=").append(grid)
                .append(", timeRelease=").append(timeRelease)
                .append(", lightnings=").append(lightnings)
                .append(", winds=").append(winds)
                .append(", precipitations=").append(precipitations)
                .append(", skies=").append(skies)
                .append(", temperatures=").append(temperatures)
                .append(", humidities=").append(humidities)
                .append('}').toString();
    }

}

final class Grid {

    final String city = null;
    final String county = null;
    final String village = null;
    final double latitude = Double.valueOf(0); // disable inlining the primitive double 0
    final double longitude = Double.valueOf(0); // disable inlining the primitive double 0

    @Override
    public String toString() {
        return new StringBuilder("Grid{")
                .append("city='").append(city).append('\'')
                .append(", county='").append(county).append('\'')
                .append(", village='").append(village).append('\'')
                .append(", latitude=").append(latitude)
                .append(", longitude=").append(longitude)
                .append('}').toString();
    }

}

final class Wind {

    final float speed;
    final float direction;

    Wind(final float speed, final float direction) {
        this.speed = speed;
        this.direction = direction;
    }

    @Override
    public String toString() {
        return new StringBuilder("Wind{")
                .append("speed=").append(speed)
                .append(", direction=").append(direction)
                .append('}').toString();
    }

}

final class Precipitation {

    final float sinceOntime;
    final int type;

    Precipitation(final float sinceOntime, final int type) {
        this.sinceOntime = sinceOntime;
        this.type = type;
    }

    @Override
    public String toString() {
        return new StringBuilder("Precipitation{")
                .append("sinceOntime='").append(sinceOntime).append('\'')
                .append(", type=").append(type)
                .append('}').toString();
    }

}

final class Sky {

    final String code;
    final String name;

    Sky(final String code, final String name) {
        this.code = code;
        this.name = name;
    }

    @Override
    public String toString() {
        return new StringBuilder("Sky{")
                .append("code='").append(code).append('\'')
                .append(", name='").append(name).append('\'')
                .append('}').toString();
    }

}

final class Common {

    @SerializedName("alertYn")
    @JsonAdapter(YnToBooleanJsonDeserializer.class)
    final boolean alert = Boolean.valueOf(false); // disable inlining the primitive boolean false

    @SerializedName("stormYn")
    @JsonAdapter(YnToBooleanJsonDeserializer.class)
    final boolean storm = Boolean.valueOf(false); // disable inlining the primitive boolean false

    @Override
    public String toString() {
        return new StringBuilder("Common{")
                .append("alert=").append(alert)
                .append(", storm=").append(storm)
                .append('}').toString();
    }

}

final class Result {

    final int code = Integer.valueOf(0); // disable inlining the primitive int 0
    final String requestUrl = null;
    final String message = null;

    @Override
    public String toString() {
        return new StringBuilder("Result{")
                .append("code=").append(code)
                .append(", requestUrl='").append(requestUrl).append('\'')
                .append(", message='").append(message).append('\'')
                .append('}').toString();
    }

}

Some of these mappings have explicit constructors - such objects must be instantiated manually in custom deserializers. If there is no constructors provided, then Gson can deal with such mapping itself having just enough information on how a particular object should be deserialized.

Since that data should be parsed in a non-standard way, a couple of custom deserializators can be implemented. The following type adapter converts "Y" and "N" to true and false respectively.

final class YnToBooleanJsonDeserializer
        implements JsonDeserializer<Boolean> {

    // Gson will instantiate this adapter itself
    private YnToBooleanJsonDeserializer() {
    }

    @Override
    public Boolean deserialize(final JsonElement jsonElement, final Type type, final JsonDeserializationContext context)
            throws JsonParseException {
        final String rawFlag = jsonElement.getAsString();
        switch ( rawFlag ) {
        case "N":
            return false;
        case "Y":
            return true;
        default:
            throw new JsonParseException("Can't parse: " + rawFlag);
        }
    }

}

The next JsonDeserializer tries to detect xxx<N>hour-like keys with regular expressions and extracting the <N> index building the lists required to construct a Forecast instance. Note that it can parse "lists" (the ones in the JSON) of arbitrary size.

final class ForecastJsonDeserializer
        implements JsonDeserializer<Forecast> {

    // This deserializer does not hold any state and can be instantiated once per application life-cycle.
    private static final JsonDeserializer<Forecast> forecastJsonDeserializer = new ForecastJsonDeserializer();

    private ForecastJsonDeserializer() {
    }

    static JsonDeserializer<Forecast> getForecastJsonDeserializer() {
        return forecastJsonDeserializer;
    }

    @Override
    public Forecast deserialize(final JsonElement jsonElement, final Type type, final JsonDeserializationContext context)
            throws JsonParseException {
        final JsonObject jsonObject = jsonElement.getAsJsonObject();
        return new Forecast(
                context.deserialize(jsonObject.get("grid"), Grid.class),
                context.deserialize(jsonObject.get("timeRelease"), Date.class),
                lightningsExtractor.parseList(jsonObject),
                windsExtractor.parseList(jsonObject.get("wind").getAsJsonObject()),
                precipitationsExtractor.parseList(jsonObject.get("precipitation").getAsJsonObject()),
                skiesExtractor.parseList(jsonObject.get("sky").getAsJsonObject()),
                temperaturesExtractor.parseList(jsonObject.get("temperature").getAsJsonObject()),
                humiditiesExtractor.parseList(jsonObject.get("humidity").getAsJsonObject())
        );
    }

    private static final AbstractExtractor<Integer> lightningsExtractor = new AbstractExtractor<Integer>(compile("lightning(\\d)hour")) {
        @Override
        protected Integer parse(final int index, final JsonObject jsonObject) {
            final String rawLightning = jsonObject.get("lightning" + index + "hour").getAsString();
            if ( rawLightning.isEmpty() ) {
                return null;
            }
            return parseInt(rawLightning);
        }
    };

    private static final AbstractExtractor<Wind> windsExtractor = new AbstractExtractor<Wind>(compile("(?:wdir|wspd)(\\d)hour")) {
        @Override
        protected Wind parse(final int index, final JsonObject jsonObject) {
            String rawSpeed = jsonObject.get("wspd" + index + "hour").getAsString();
            String rawDirection = jsonObject.get("wdir" + index + "hour").getAsString();
            if ( rawSpeed.isEmpty() && rawDirection.isEmpty() ) {
                return null;
            }
            return new Wind(parseFloat(rawSpeed), parseFloat(rawDirection));
        }
    };

    private static final AbstractExtractor<Precipitation> precipitationsExtractor = new AbstractExtractor<Precipitation>(compile("(?:sinceOntime|type)(\\d)hour")) {
        @Override
        protected Precipitation parse(final int index, final JsonObject jsonObject) {
            final String rawSinceOntime = jsonObject.get("sinceOntime" + index + "hour").getAsString();
            final String rawType = jsonObject.get("type" + index + "hour").getAsString();
            if ( rawSinceOntime.isEmpty() && rawType.isEmpty() ) {
                return null;
            }
            return new Precipitation(parseFloat(rawSinceOntime), parseInt(rawType));
        }
    };

    private static final AbstractExtractor<Sky> skiesExtractor = new AbstractExtractor<Sky>(compile("(?:code|name)(\\d)hour")) {
        @Override
        protected Sky parse(final int index, final JsonObject jsonObject) {
            final String rawCode = jsonObject.get("code" + index + "hour").getAsString();
            final String rawName = jsonObject.get("name" + index + "hour").getAsString();
            if ( rawCode.isEmpty() && rawName.isEmpty() ) {
                return null;
            }
            return new Sky(rawCode, rawName);
        }
    };

    private static final AbstractExtractor<Float> temperaturesExtractor = new AbstractExtractor<Float>(compile("temp(\\d)hour")) {
        @Override
        protected Float parse(final int index, final JsonObject jsonObject) {
            final String rawTemperature = jsonObject.get("temp" + index + "hour").getAsString();
            if ( rawTemperature.isEmpty() ) {
                return null;
            }
            return parseFloat(rawTemperature);
        }
    };

    private static final AbstractExtractor<Float> humiditiesExtractor = new AbstractExtractor<Float>(compile("rh(\\d)hour")) {
        @Override
        protected Float parse(final int index, final JsonObject jsonObject) {
            final String rawHumidity = jsonObject.get("rh" + index + "hour").getAsString();
            if ( rawHumidity.isEmpty() ) {
                return null;
            }
            return parseFloat(rawHumidity);
        }
    };

    private abstract static class AbstractExtractor<T> {

        private final Pattern pattern;

        private AbstractExtractor(final Pattern pattern) {
            this.pattern = pattern;
        }

        protected abstract T parse(int index, JsonObject jsonObject);

        private List<T> parseList(final JsonObject jsonObject) {
            final List<T> list = new ArrayList<>();
            for ( final Entry<String, JsonElement> e : jsonObject.entrySet() ) {
                final String key = e.getKey();
                final Matcher matcher = pattern.matcher(key);
                // Check if the given regular expression matches the key
                if ( matcher.matches() ) {
                    // If yes, then just extract and parse the index 
                    final int index = parseInt(matcher.group(1));
                    // And check if there is enough room in the result list because the JSON response may contain unordered keys
                    while ( index > list.size() ) {
                        list.add(null);
                    }
                    // As Java lists are 0-based
                    if ( list.get(index - 1) == null ) {
                        // Assuming that null marks an object that's probably not parsed yet
                        list.set(index - 1, parse(index, jsonObject));
                    }
                }
            }
            return list;
        }

    }

}

And now it all can be put together:

public static void main(final String... args) {
    final Gson gson = new GsonBuilder()
            .setDateFormat("yyyy-MM-dd hh:mm:ss")
            .registerTypeAdapter(Forecast.class, getForecastJsonDeserializer())
            .create();
    final Response response = gson.fromJson(JSON, Response.class);
    System.out.println(response);
}

The output:

Response{weather=Weather{forecast3hours=[Forecast{grid=Grid{city='충북', county='충주시', village='목행동', latitude=37.01356, longitude=127.90365}, timeRelease=Fri Feb 24 16:30:00 EET 2017, lightnings=[0, 0, null, null], winds=[Wind{speed=4.2, direction=179.0}, Wind{speed=3.1, direction=176.0}, null, null], precipitations=[Precipitation{sinceOntime='0.0', type=0}, Precipitation{sinceOntime='0.0', type=0}, null, null], skies=[Sky{code='SKY_V01', name='맑음'}, Sky{code='SKY_V01', name='맑음'}, null, null], temperatures=[3.2, 2.0, null, null], humidities=[41.0, 50.0, null, null]}]}, common=Common{alert=false, storm=false}, result=Result{code=9200, requestUrl='/weather/forecast/3hours?lon=127.9259&lat=36.991&version=1&appKey=4ce0462a-3884-30ab-ab13-93efb1bc171f', message='성공'}}

这篇关于如何在Android中使用基于动态SerializedName的gson解析json对象的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆