Flutter如何对数据表单API进行JSON序列化

时间:2019-08-26 11:06:32

标签: json flutter dart

我正在尝试在flutter应用程序中使用模型,以使该概念在我的应用程序中运行。我是OOP的新手,所以我想学习更多。我的问题是我有来自OpenWeather API的API响应,我的方法是调用api获取数据。效果很好,我通常使用JSON手动进行人工解码并通过人工天气[“ main”]来访问属性。但是我想要按模型分解/预处理我的JSON。下面的代码运行良好,但是我想应用使用JSON序列化的概念,我不知道如何开始。我所有的尝试都失败了...:/

在之前的回答中,我已经借助小伙子的帮助https://app.quicktype.io/生成了模型。

型号:

import 'dart:convert';

Forcast forcastFromJson(String str) => Forcast.fromJson(json.decode(str));

String forcastToJson(Forcast data) => json.encode(data.toJson());

class Forcast {
  String cod;
  double message;
  int cnt;
  List<ListElement> list;
  City city;

  Forcast({
    this.cod,
    this.message,
    this.cnt,
    this.list,
    this.city,
  });

  factory Forcast.fromJson(Map<String, dynamic> json) => new Forcast(
        cod: json["cod"],
        message: json["message"].toDouble(),
        cnt: json["cnt"],
        list: new List<ListElement>.from(
            json["list"].map((x) => ListElement.fromJson(x))),
        city: City.fromJson(json["city"]),
      );

  Map<String, dynamic> toJson() => {
        "cod": cod,
        "message": message,
        "cnt": cnt,
        "list": new List<dynamic>.from(list.map((x) => x.toJson())),
        "city": city.toJson(),
      };
}

class City {
  int id;
  String name;
  Coord coord;
  String country;

  City({
    this.id,
    this.name,
    this.coord,
    this.country,
  });

  factory City.fromJson(Map<String, dynamic> json) => new City(
        id: json["id"],
        name: json["name"],
        coord: Coord.fromJson(json["coord"]),
        country: json["country"],
      );

  Map<String, dynamic> toJson() => {
        "id": id,
        "name": name,
        "coord": coord.toJson(),
        "country": country,
      };
}

class Coord {
  double lat;
  double lon;

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

  factory Coord.fromJson(Map<String, dynamic> json) => new Coord(
        lat: json["lat"].toDouble(),
        lon: json["lon"].toDouble(),
      );

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

class ListElement {
  int dt;
  MainClass main;
  List<Weather> weather;
  Clouds clouds;
  Wind wind;
  Sys sys;
  DateTime dtTxt;
  Rain rain;
  Rain snow;

  ListElement({
    this.dt,
    this.main,
    this.weather,
    this.clouds,
    this.wind,
    this.sys,
    this.dtTxt,
    this.rain,
    this.snow,
  });

  factory ListElement.fromJson(Map<String, dynamic> json) => new ListElement(
        dt: json["dt"],
        main: MainClass.fromJson(json["main"]),
        weather: new List<Weather>.from(
            json["weather"].map((x) => Weather.fromJson(x))),
        clouds: Clouds.fromJson(json["clouds"]),
        wind: Wind.fromJson(json["wind"]),
        sys: Sys.fromJson(json["sys"]),
        dtTxt: DateTime.parse(json["dt_txt"]),
        rain: json["rain"] == null ? null : Rain.fromJson(json["rain"]),
        snow: json["snow"] == null ? null : Rain.fromJson(json["snow"]),
      );

  Map<String, dynamic> toJson() => {
        "dt": dt,
        "main": main.toJson(),
        "weather": new List<dynamic>.from(weather.map((x) => x.toJson())),
        "clouds": clouds.toJson(),
        "wind": wind.toJson(),
        "sys": sys.toJson(),
        "dt_txt": dtTxt.toIso8601String(),
        "rain": rain == null ? null : rain.toJson(),
        "snow": snow == null ? null : snow.toJson(),
      };
}

class Clouds {
  int all;

  Clouds({
    this.all,
  });

  factory Clouds.fromJson(Map<String, dynamic> json) => new Clouds(
        all: json["all"],
      );

  Map<String, dynamic> toJson() => {
        "all": all,
      };
}

class MainClass {
  double temp;
  double tempMin;
  double tempMax;
  double pressure;
  double seaLevel;
  double grndLevel;
  int humidity;
  double tempKf;

  MainClass({
    this.temp,
    this.tempMin,
    this.tempMax,
    this.pressure,
    this.seaLevel,
    this.grndLevel,
    this.humidity,
    this.tempKf,
  });

  factory MainClass.fromJson(Map<String, dynamic> json) => new MainClass(
        temp: json["temp"].toDouble(),
        tempMin: json["temp_min"].toDouble(),
        tempMax: json["temp_max"].toDouble(),
        pressure: json["pressure"].toDouble(),
        seaLevel: json["sea_level"].toDouble(),
        grndLevel: json["grnd_level"].toDouble(),
        humidity: json["humidity"],
        tempKf: json["temp_kf"].toDouble(),
      );

  Map<String, dynamic> toJson() => {
        "temp": temp,
        "temp_min": tempMin,
        "temp_max": tempMax,
        "pressure": pressure,
        "sea_level": seaLevel,
        "grnd_level": grndLevel,
        "humidity": humidity,
        "temp_kf": tempKf,
      };
}

class Rain {
  double the3H;

  Rain({
    this.the3H,
  });

  factory Rain.fromJson(Map<String, dynamic> json) => new Rain(
        the3H: json["3h"] == null ? null : json["3h"].toDouble(),
      );

  Map<String, dynamic> toJson() => {
        "3h": the3H == null ? null : the3H,
      };
}

class Sys {
  Pod pod;

  Sys({
    this.pod,
  });

  factory Sys.fromJson(Map<String, dynamic> json) => new Sys(
        pod: podValues.map[json["pod"]],
      );

  Map<String, dynamic> toJson() => {
        "pod": podValues.reverse[pod],
      };
}

enum Pod { D, N }

final podValues = new EnumValues({"d": Pod.D, "n": Pod.N});

class Weather {
  int id;
  MainEnum main;
  Description description;
  String icon;

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

  factory Weather.fromJson(Map<String, dynamic> json) => new Weather(
        id: json["id"],
        main: mainEnumValues.map[json["main"]],
        description: descriptionValues.map[json["description"]],
        icon: json["icon"],
      );

  Map<String, dynamic> toJson() => {
        "id": id,
        "main": mainEnumValues.reverse[main],
        "description": descriptionValues.reverse[description],
        "icon": icon,
      };
}

enum Description {
  CLEAR_SKY,
  BROKEN_CLOUDS,
  LIGHT_RAIN,
  MODERATE_RAIN,
  FEW_CLOUDS
}

final descriptionValues = new EnumValues({
  "broken clouds": Description.BROKEN_CLOUDS,
  "clear sky": Description.CLEAR_SKY,
  "few clouds": Description.FEW_CLOUDS,
  "light rain": Description.LIGHT_RAIN,
  "moderate rain": Description.MODERATE_RAIN
});

enum MainEnum { CLEAR, CLOUDS, RAIN }

final mainEnumValues = new EnumValues({
  "Clear": MainEnum.CLEAR,
  "Clouds": MainEnum.CLOUDS,
  "Rain": MainEnum.RAIN
});

class Wind {
  double speed;
  double deg;

  Wind({
    this.speed,
    this.deg,
  });

  factory Wind.fromJson(Map<String, dynamic> json) => new Wind(
        speed: json["speed"].toDouble(),
        deg: json["deg"].toDouble(),
      );

  Map<String, dynamic> toJson() => {
        "speed": speed,
        "deg": deg,
      };
}

class EnumValues<T> {
  Map<String, T> map;
  Map<T, String> reverseMap;

  EnumValues(this.map);

  Map<T, String> get reverse {
    if (reverseMap == null) {
      reverseMap = map.map((k, v) => new MapEntry(v, k));
    }
    return reverseMap;
  }
}

进行API调用的网络:

import 'package:http/http.dart' as http;
import 'dart:convert';

class NetworkHelper {
  NetworkHelper({this.text});

  String text;
  String apiKey = '';

  Future<dynamic> getData(text) async {
    http.Response response = await http.get(
        'https://api.openweathermap.org/data/2.5/weather?q=$text&appid=$apiKey&units=metric');

    if (response.statusCode == 200) {
      var decodedData = jsonDecode(response.body);

      return decodedData;
    } else {
      print(response.statusCode);
    }
  }

  Future<dynamic> getForcast(text) async {
    http.Response response = await http.get(
        'http://api.openweathermap.org/data/2.5/forecast?q=${text}&units=metric&appid=$apiKey');
    if (response.statusCode == 200) {
      var decodedData = jsonDecode(response.body);

      return decodedData;
    } else {
      print(response.statusCode);
    }
  }

  Future<dynamic> getDataLocation(lat, lon) async {
    http.Response response = await http.get(
        'https://api.openweathermap.org/data/2.5/weather?lat=$lat&lon=$lon&appid=$apiKey&units=metric');

    if (response.statusCode == 200) {
      var decodedData = jsonDecode(response.body);

      return decodedData;
    } else {
      print(response.statusCode);
    }
  }

  Future<dynamic> getForcastLocation(lat, lon) async {
    http.Response response = await http.get(
        'http://api.openweathermap.org/data/2.5/forecast?lat=$lat&lon=$lon&units=metric&appid=$apiKey');
    if (response.statusCode == 200) {
      var decodedData = jsonDecode(response.body);

      return decodedData;
    } else {
      print(response.statusCode);
    }
  }
}

我显示数据的天气:

import 'package:flutter/material.dart';
import 'package:weather/common/format.dart';

import 'package:weather/service/Network.dart';
import 'package:weather/service/location.dart';

class Weather extends StatefulWidget {
  Weather({this.text});
  final String text;

  _WeatherState createState() => _WeatherState();
}

class _WeatherState extends State<Weather> {
  NetworkHelper networkHelper = NetworkHelper();
  Location location = Location();
  Formats formats = Formats();
  int temperature;
  String cityName;
  String description;
  bool isLoading = true;
  dynamic newData;
  String city;
  @override
  void initState() {
    super.initState();
    city = widget.text;
    buildUI(city);
  }

  buildUI(String text) async {
    var weatherData = await networkHelper.getData(text);
    var forecastData = await networkHelper.getForcast(text);
    double temp = weatherData['main']['temp'];
    temperature = temp.toInt();
    cityName = weatherData['name'];
    description = weatherData['weather'][0]['description'];
    newData = forecastData['list'].toList();
    setState(() {
      isLoading = false;
    });
  }

  buildUIByLocation() async {
    await location.getCurrentLocation();
    var weatherLocation = await networkHelper.getDataLocation(
        location.latitude, location.longitude);
    var forcastLocation = await networkHelper.getForcastLocation(
        location.latitude, location.longitude);
    double temp = weatherLocation['main']['temp'];
    temperature = temp.toInt();
    cityName = weatherLocation['name'];
    description = weatherLocation['weather'][0]['description'];
    newData = forcastLocation['list'].toList();
    setState(() {
      isLoading = false;
    });
  }

  Widget get _pageToDisplay {
    if (isLoading == true) {
      return _loadingView;
    } else {
      return _weatherView;
    }
  }

  Widget get _loadingView {
    return Center(child: CircularProgressIndicator());
  }

  Widget get _weatherView {
    return SafeArea(
      child: Column(
        children: <Widget>[
          Flexible(
            flex: 1,
            child: Container(
              margin: EdgeInsets.fromLTRB(12, 1, 30, 0),
              decoration: new BoxDecoration(
                color: Color(0xff4556FE),
                borderRadius: BorderRadius.all(Radius.circular(10.0)),
                boxShadow: [
                  BoxShadow(
                    color: Color(0xFFD4DAF6),
                    offset: Offset(20, 20),
                  ),
                  BoxShadow(
                    color: Color(0xFFadb6ff),
                    offset: Offset(10, 10),
                  ),
                ],
              ),
              child: Row(
                crossAxisAlignment: CrossAxisAlignment.center,
                mainAxisAlignment: MainAxisAlignment.center,
                children: <Widget>[
                  Expanded(
                    child: Column(
                      mainAxisAlignment: MainAxisAlignment.center,
                      crossAxisAlignment: CrossAxisAlignment.center,
                      children: <Widget>[
                        Text(
                          '$cityName',
                          style: TextStyle(fontSize: 25, color: Colors.white),
                        ),
                        SizedBox(
                          height: 5,
                        ),
                        Text(
                          '$temperature°C',
                          style: TextStyle(fontSize: 50, color: Colors.white),
                        ),
                      ],
                    ),
                  ),
                  Expanded(
                    child: Column(
                      mainAxisAlignment: MainAxisAlignment.center,
                      crossAxisAlignment: CrossAxisAlignment.center,
                      children: <Widget>[
                        Text(
                          '$description',
                          style: TextStyle(fontSize: 25, color: Colors.white),
                        ),
                      ],
                    ),
                  )
                ],
              ),
            ),
          ),
          SizedBox(
            height: 30,
          ),
          Flexible(
            flex: 2,
            child: Container(
              margin: EdgeInsets.fromLTRB(12, 10, 12, 0),
              decoration: new BoxDecoration(
                color: Color(0xff4556FE),
                borderRadius: BorderRadius.vertical(top: Radius.circular(10.0)),
              ),
              child: ListView.builder(
                  padding: const EdgeInsets.all(8.0),
                  itemCount: newData.length,
                  itemBuilder: (BuildContext context, int index) {
                    return Container(
                        margin: const EdgeInsets.all(4.0),
                        height: 50,
                        child: Center(
                          child: Row(
                            crossAxisAlignment: CrossAxisAlignment.center,
                            mainAxisAlignment: MainAxisAlignment.spaceAround,
                            children: <Widget>[
                              Text(
                                formats.readTimeStamp(newData[index]['dt']),
                                style: TextStyle(
                                    color: Colors.white, fontSize: 14),
                              ),
                              Text(
                                newData[index]['weather'][0]['main'].toString(),
                                style: TextStyle(
                                    color: Colors.white,
                                    fontSize: 18,
                                    fontWeight: FontWeight.w600),
                              ),
                              Text(
                                formats.floatin(newData[index]['main']['temp']),
                                style: TextStyle(
                                    color: Colors.white,
                                    fontSize: 16,
                                    fontWeight: FontWeight.w600),
                              ),
                            ],
                          ),
                        ));
                  }),
            ),
          ),
        ],
      ),
    );
  }

  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        actions: <Widget>[
          IconButton(
            padding: EdgeInsets.fromLTRB(0, 0, 20, 0),
            icon: Icon(Icons.autorenew, color: Colors.black, size: 30),
            onPressed: () {
              if (city == "") {
                setState(() {
                  isLoading = true;
                  buildUIByLocation();
                });
              } else {
                setState(() {
                  isLoading = true;
                  buildUI(city);
                });
              }
            },
          ),
          IconButton(
            padding: EdgeInsets.fromLTRB(0, 0, 15, 0),
            icon: Icon(
              Icons.location_on,
              color: Colors.black,
              size: 30,
            ),
            onPressed: () async {
              setState(() {
                city = '';
                isLoading = true;
              });
              await buildUIByLocation();
            },
          )
        ],
        leading: IconButton(
          icon: Icon(Icons.arrow_back_ios, color: Colors.black),
          onPressed: () {
            Navigator.pop(context);
          },
        ),
        elevation: 0,
        backgroundColor: Colors.transparent,
        title: const Text(
          'Change location',
          style: TextStyle(color: Colors.black),
        ),
      ),
      body: Stack(
        children: <Widget>[
          Container(
            decoration: BoxDecoration(color: Color(0xFFfafafa)),
            child: Padding(
              padding: const EdgeInsets.fromLTRB(5, 20, 5, 0),
              child: Center(child: _pageToDisplay),
            ),
          )
        ],
      ),
    );
  }
}

2 个答案:

答案 0 :(得分:2)

这是一个非常基本的示例。在这里,您可以看到Car模型类及其基本属性。数据将由CarRepository类获取,该类进行网络工作并将数据从json映射到模型。 CarScreen类是一个有状态的小部件,在initState之后调用CarRepository。如果从网络获取数据,则汽车将显示在列表中。 提取时会显示加载指示符。

import 'dart:convert';
import 'package:http/http.dart' as http;

class Car {
  //
  // Attributes
  //
  int id;
  String name;
  String color;
  int speed;

  //
  // Constructor
  //
  Car({
    @required this.id,
    @required this.name,
    @required this.color,
    @required this.speed,
  });

  // convert Json to an car object object
  factory Car.fromJson(Map<String, dynamic> json) {
    return Car(
      id: json['id'] as int,
      name: json['name'] as String,
      color: json['color'] as String,
      speed: json['speed'] as int,
    );
  }
}

class CarRepository {
  /// Load all cars form api and will convert it
  /// to an list of cars.
  ///
  ///  {
  ///     "results": [
  ///         {
  ///           "id": 1,
  ///           "name": "Tesla Model 3",
  ///           "color": "red",
  ///           "speed": 225
  ///         },
  ///         {
  ///           "id": 3,
  ///           "name": "Tesla Model S",
  ///           "color": "black",
  ///           "speed": 255
  ///         }
  ///     ]
  ///  }
  ///
  ///
  static Future<List<Car>> fetchAll() async {
    String query = '';
    String url = Uri.encodeFull("https://xxx.de/api/cars" + query);
    final response = await http.get(url);

    if (response.statusCode == 200) {
      Map data = json.decode(response.body);
      final cars = (data['results'] as List).map((i) => new Car.fromJson(i));
      return cars.toList();
    } else {
      return [];
    }
  }
}

class CarsScreen extends StatefulWidget {
  _CarsScreenState createState() => _CarsScreenState();
}

class _CarsScreenState extends State<CarsScreen> {
  bool _isLoading = true;
  List<Car> _cars = [];

  @override
  void initState() {
    super.initState();
    fetchCars();
  }

  Future fetchCars() async {
    _cars = await CarRepository.fetchAll();
    setState(() {
      _isLoading = false;
    });
  }

  @override
  Widget build(BuildContext context) {
    if (_isLoading) {
      return Scaffold(
        appBar: AppBar(
          title: Text('Cars'),
        ),
        body: Container(
          child: Center(
            child: CircularProgressIndicator(),
          ),
        ),
      );
    } else {
      return Scaffold(
        appBar: AppBar(
          title: Text('Cars'),
        ),
        body: ListView.builder(
          itemCount: _cars.length,
          itemBuilder: (context, index) {
            Car car = _cars[index];
            return ListTile(
              title: Text(car.name),
              subtitle: Text(car.color),
            );
          },
        ),
      );
    }
  }
}

答案 1 :(得分:1)

此示例包括带有汽车对象的嵌套数组。希望对您有所帮助。

生产者与汽车之间存在某种关系。

import 'dart:convert';
import 'package:http/http.dart' as http;


class CarRepository {
  /// Load all producers form api and will convert it
  /// to an list of producers.
  ///   [
  ///     {
  ///       "id": 1,
  ///       "name": "Tesla"
  ///       "cars": [
  ///           {
  ///           "id": 1,
  ///           "name": "Tesla Model 3",
  ///           "color": "red",
  ///           "speed": 225
  ///         },
  ///         {
  ///           "id": 3,
  ///           "name": "Tesla Model S",
  ///           "color": "black",
  ///           "speed": 255
  ///         }
  ///         ]
  ///     },
  ///     {
  ///       "id": 2,
  ///       "name": "Volkswagen"
  ///       "cars": [
  ///           {
  ///             "id": 1,
  ///             "name": "Golf",
  ///             "color": "red",
  ///             "speed": 225
  ///           },
  ///           {
  ///             "id": 3,
  ///             "name": "Passat",
  ///             "color": "black",
  ///             "speed": 255
  ///           }
  ///        ]
  ///     }
  ///   ]
  ///
  ///
  static Future<List<Car>> fetchAll() async {
    String query = '';
    String url = Uri.encodeFull("https://xxx.de/api/producers" + query);
    final response = await http.get(url);

    if (response.statusCode == 200) {
      Map data = json.decode(response.body);
      final cars = (data as List).map((i) => new Car.fromJson(i));
      return cars.toList();
    } else {
      return [];
    }
  }
}



class Producer {
  //
  // Attributes
  //
  int id;
  String name;
  List<Car> cars;

  //
  // Constructor
  //
  Producer({
    @required this.id,
    @required this.name,
    @required this.cars,
  });

  // convert Json to an producer object object
  factory Producer.fromJson(Map<String, dynamic> json) {
    return Producer(
      id: json['id'] as int,
      name: json['name'] as String,
      cars: (json['cars'] as List ?? []).map((c) {
        return Car.fromJson(c);
      }).toList(),
    );
  }
}

class Car {
  //
  // Attributes
  //
  int id;
  String name;
  String color;
  int speed;

  //
  // Constructor
  //
  Car({
    @required this.id,
    @required this.name,
    @required this.color,
    @required this.speed,
  });

  // convert Json to an car object object
  factory Car.fromJson(Map<String, dynamic> json) {
    return Car(
      id: json['id'] as int,
      name: json['name'] as String,
      color: json['color'] as String,
      speed: json['speed'] as int,
    );
  }
}