我如何使用Gson这个网址解析这个json物件: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": "" },
我如何为该json对象编写Java模型。我不想写每个这样的变量。
class Wind{ @SerializedName("wdir1hour") private String wdir1hour; @SerializedName("wdir2hour") private String wdir2hour; @SerializedName("wdir3hour") private String wdir3hour; @SerializedName("wdir4hour") private String wdir4hour; }
谁对这个json对象有更好的主意?
通过您提供的链接可访问的当前JSON响应似乎存在一些设计问题或怀疑。我将在此处发布JSON,以免将来丢失它:
{ "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": "성공" } }
从我的角度来看,它们是:
null
Yn
true
false
"Y"
"N"
这就是为什么自动POJO生成器可能不是最佳的处理方式,因为它们可能无法检测到特定JSON字符串值的“真实”类型,而且它们无法生成自定义反序列化器。不知道为什么要这样设计,但是您可以设计自定义映射,使其对程序更加友好,并且可以对其进行更多控制。
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(); } }
其中一些映射具有显式构造函数- 必须在自定义反序列化器中手动实例化此类对象。如果没有提供构造函数,那么Gson可以处理这种映射本身,而该映射只具有有关应如何反序列化特定对象的足够信息。
由于应该以非标准方式解析数据,因此可以实现几个自定义反序列化器。以下类型适配器分别将"Y"和转换"N"为true和false。
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); } } }
下一个JsonDeserializer尝试xxx<N>hour使用正则表达式检测类似键,并提取<N>索引以构建构造Forecast实例所需的列表。请注意,它可以解析任意大小的“列表”(JSON中的列表)。
JsonDeserializer
xxx<N>hour
<N>
Forecast
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; } } }
现在可以将它们放在一起:
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); }
输出:
响应{天气=天气{forecast3hours = [预测{grid = Grid {city =’충북’,县=’충주시’,村庄=’목행동’,纬度= 37.01356,经度= 127.90365}},timeRelease =周五2月24日30:30 :00 EET 2017,闪电= [0,0,null,null],风= [风{speed = 4.2,方向= 179.0},风{speed = 3.1,方向= 176.0},null,null],降水量= [降水{sinceOntime =‘0.0’,类型= 0},降水{sinceOntime =‘0.0’,类型= 0},null,null],天空= [Sky {code =’SKY_V01’,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 =’성공’}}