[flutter] #2 json handling - nested structure

giyeon·2021년 5월 10일
1

flutter-json handling

목록 보기
2/3
post-thumbnail

#이 포스팅은 플린이의 입장에서 쓰여진 글입니다. 코드 지적은 언제나 환영입니다. 🙆🏻‍♂️

이 프로젝트는 Youtube '헤비프랜 - Heavy Fran'의 'Flutter json handling - json_serializable-플러터에서 json_serializable 패키지를 사용해 json 데이터 처리하는 방법' 강의를 참고했습니다.


Nested Structure

이번 포스팅은 Flat Structure에 이어서 Nested Structure에 대해서 다뤄볼게요.
똑같이 아래의 json data를 사용해요.

{
"coord": {
"lon": 126.9778,
"lat": 37.5683
},
"weather": [
{
"id": 804,
"main": "Clouds",
"description": "overcast clouds",
"icon": "04n"
}
],
"base": "stations",
"main": {
"temp": 283.79,
"feels_like": 282.89,
"temp_min": 283.79,
"temp_max": 283.79,
"pressure": 1015,
"humidity": 76,
"sea_level": 1015,
"grnd_level": 1008
},
"visibility": 10000,
"wind": {
"speed": 1.39,
"deg": 84,
"gust": 1.64
},
"clouds": {
"all": 91
},
"dt": 1620663273,
"sys": {
"type": 1,
"id": 8093,
"country": "KR",
"sunrise": 1620678396,
"sunset": 1620729024
},
"timezone": 32400,
"id": 1835848,
"name": "Seoul",
"cod": 200
}

Nested structure는 nest된 object들을 각각 class로 정의하는걸 말해요.
다시말해서 위 json data의 coord, weather... 를 따로 class를 만들어줘요.

먼저 coord를 class로 만들어보면 아래와 같아요.

Coord class

class Coord {
  final double lon;
  final double lat;

  Coord({this.lon, this.lat});

  factory Coord.fromJson(Map<String, dynamic> json) {
    return Coord(
      lon: json['lon'],
      lat: json['lat'],
    );
  }

  Map<String, dynamic> toJson() {
    return {
      "lon": lon,
      "lat": lat,
    };
  }
}

class 내부에 각각 fromJson, toJson을 별도로 정의해요.

json을 보면 Weather data는 List로 감싸져 있어요.
리스트 안의 각 item들을 다룰거라 List는 크게 신경을 안쓰고 class를 만들어줘요.

Weather class

class Weather {
  final int id;
  final String main;
  final String description;
  final String icon;

  Weather({this.id, this.main, this.description, this.icon});

  factory Weather.fromJson(Map<String, dynamic> json) {
    return Weather(
      id: json['id'],
      main: json['main'],
      description: json['description'],
      icon: json['icon'],
    );
  }

  Map<String, dynamic> toJson() {
    return {
      "id": id,
      "main": main,
      "description": description,
      "icon": icon,
    };
  }
}

Main쪽도 동일하게 클래스를 만들어줘요.

Main class

  Main({
    this.temp,
    this.feelsLike,
    this.tempMin,
    this.tempMax,
    this.pressure,
    this.humidity,
  });

  factory Main.fromJson(Map<String, dynamic> json) {
    return Main(
      temp: json['temp'],
      feelsLike: json['feels_like'],
      tempMin: json['temp_min'],
      tempMax: json['temp_max'],
      pressure: json['pressure'],
      humidity: json['humidity'],
    );
  }

  Map<String, dynamic> toJson() {
    return {
      "temp": temp,
      "feelsLike": feelsLike,
      "tempMin": tempMin,
      "tempMax": tempMax,
      "pressure": pressure,
      "humidity": humidity,
    };
  }
}

visibility와 같이 단일 값을 가지는 item들도 따로 class를 만들어줘야 하는것은 아니에요.
후에 나오는 코드를 보면 이해하실 수 있을거에요.

이제 정의한 class들을 모두 포함하는 class를 정의해요.
이름은 OldNestedWeather라고 해볼게요.

OldNestedWeather class

class OldNestedWeather {
  final Coord coord;
  final List<Weather> weather;
  final Main main;
  final int visibility;

  OldNestedWeather({
    this.coord,
    this.weather,
    this.main,
    this.visibility,
  });

  factory OldNestedWeather.fromJson(Map<String, dynamic> json) {
    return OldNestedWeather(
      coord: Coord.fromJson(json['coord']),
      main: Main.fromJson(json['main']),
      visibility: json['visibility'],
      weather: (json['weather'] as List)
          .map((e) => e == null ? null : Weather.fromJson(e))
          .toList(),
    );
  }

  Map<String, dynamic> toJson() {
    return {
      "coord": coord.toJson(),
      "weather": weather.map((e) => e.toJson()).toList(),
      "main": main.toJson(),
      "visibility": visibility,
    };
  }
}

필드값으로 Coord, Weather, Main class를 포함하고 단일값을 가지는 visibility는 int로 가져와요.

fromJson method를 보면 coord, main, visibility 정도는 json 맵핑이 비교적 간단한 편이에요.
하지만 weather 부분은 json data에서 List로 감싸져있기 때문에 map 처리를 한번 거쳐야해요.

json['weather']

를하게 되면 List를 가져오게 돼요.
이 List를 map 처리해서 안쪽 원소를 가져와야 하기떄문에 map 처리를 해줘요.

이렇게해서 Nested structure의 class 처리는 끝났어요.

확실히 Flat보다 구조가 조금이나마 간단해지긴 했지만..
class마다 fromJson, toJson을 정의해햐하는게 조금 까다로워요.

이제 data를 screen에 불러와볼게요.

NestedWeatherScreen

Future<OldNestedWeather> getWeather() async {
    try {
      const String url =
          'https://api.openweathermap.org/data/2.5/weather?q=seoul&appid=$KEY';

      final http.Response response = await http.get(url);
      final responseData = json.decode(response.body);
      final OldNestedWeather onw = OldNestedWeather.fromJson(responseData);

      print(onw.toJson());

      return onw;
    } catch (err) {
      print(err);
      throw err;
    }
  }

Future getWeather method는 Flat과 동일해요.

 return Scaffold(
      appBar: AppBar(
        title: Text('Nested Weather'),
      ),
      body: FutureBuilder(
        future: getWeather(),
        builder: (context, snapshot) {
          if (snapshot.hasData) {
            final onw = snapshot.data;
            return ListView(...

다만 아래와같이 snapshot의 value에 접근 시, nested는 각각을 class화 했기 때문에 flat과는 조금 다른 형식으로 불러올 수 있어요.

Nested

'longitude: ${onw.coord.lon}',

Flat

'longitude: ${ofw.coordLon}',

여기까지 Nested Structure에 대해서 알아봤어요.
다음 포스팅은 끝판왕인 json_serializable에 대해 알아보도록 할게요 🙌

profile
Web , App developer wannabe 🧑🏻‍💻

1개의 댓글

comment-user-thumbnail
2022년 12월 22일

아주 좋네요

답글 달기