如何映射作为 Dart 中其他模型基础的复杂模型

问题描述 投票:0回答:2

我有一个模型,我想从 HTTP 请求中获取响应并映射它。问题是这个模型有一个可以接受任何属性的属性,但我想正确映射它。

class BaseResponse<T> {
  final bool error;
  final String? message;
  final T? result;

  const BaseResponse({required this.error, this.message, this.result});

  factory BaseResponse.fromJson(Map<String, dynamic> json) {
    bool error = json['error'];
    String? message = json['message'];
    T? result = json['result'];

    return BaseResponse(error: error, message: message, result: result);
  }
}

我想找到一种方法,可以从 JSON 中映射正确的数据作为结果属性。结果属性可以是一个复杂的模型,也可以是像

String
这样简单的东西。

class SampleModel {
    final String firstName;
    final String lastName;
    final AddressModel address;

    SampleModel({ required this.firstName, required this.lastName, required this.address });
}

class AddressModel {
    final String street;
    final String city;
    // ... more properties

    AddressModel({ required this.street, required this.city, /* More properties here */ });
}

void main() {
    Future<void> getResponse() async {
        Future<http.Response> response = await http.get('https://getsomething.com');

        // Do some checks against response

        BaseResponse<SampleModel> = BaseResponse.fromJson(jsonDecode(response.body) as Map<String, dynamic>);
    }

    getResponse();
}
dart oop generics
2个回答
1
投票

如果您知道您想要一个

BaseResponse<SampleModel>
,并且您需要为
SampleModel
做一些特定的事情,那么您必须调用知道要做什么的代码,或者告诉该代码要做什么。

我要做的就是向

SampleModel.fromJson
添加一个函数参数,将
json[result]
转换为所需的类型,然后将所需结果类型的解码器传递给它:

  factory BaseResponse.fromJson(Map<String, dynamic> json, 
      T? Function(Object?) parseResult) {
    bool error = json['error'];
    String? message = json['message'];
    T? result = parseResult(json['result']);
    return BaseResponse(error: error, message: message, result: result);
  }

SampleModel
声明一个解码器:

static SampleModel fromJson(Object? result) {
  if (result case {
      "firstName": String first,  
      "lastName": String last,
      "address": Map<String, Object?> addressJson,
      // ..
    }) {
      var address = AddressModel.fromJson(addressJson);
      if (address != null) {
        return SampleModel(firstName: first, lastName: last, address: address);
      }
    }
    return null;
  }

并在调用时传递该函数

fromJson
:

      // Do some checks against response
      BaseResponse<SampleModel> model = BaseResponse.fromJson(
          jsonDecode(response.body) as Map<String, dynamic>,
          SampleModel.fromJson); // <-- added here.

BaseResponse
由类型参数化。在
BaseResponse
类内部,泛型类型参数应该被视为 generally。也就是说,如果代码不应该依赖于实际绑定到
T
的类型,它对于任何类型都应该是相同的。

这确实意味着任何特定类型行为都必须从

BaseResponse
类外部提供。 Dart 不允许通过类型参数调用静态函数, 因此,任何需要为每个实际类型参数提供不同行为的功能都需要传递到类中。无论是在需要时还是在构造函数中。

另一种削减抽象的方法可以是传入结果, 并从另一个辅助函数调用

BaseResponse.fromJson

class BaseResponse<T extends Object> {
  ...
  BaseResponse.fromJson(Map<String, dynamic> json, T? result) :
    this(error: json['error'], message: json['message'], result: result);
}

class SampleModel {
  static SampleModel? fromJson(Map<String, dynamic> json) {
    // Parse firstName/lastName/etc as above.
    return sample;
  }

  static BaseResponse<SampleModel> baseFromJson(Map<String, dynamic> json) {
    SampleModel? result = fromJson(json);
    return BaseResponse<SampleModel>(json, result);
  }
}

然后这将由如下代码调用:

  // Do some checks against response.
  BaseResponse<SampleModel> model = SampleModel.baseFromJson(json);

这还选择一个代码路径,该代码路径为检查所说使用的

SampleModel
执行特定操作,并使用通用
BaseResponse
的代码,其输入取决于泛型类型。 它只是传递一个值而不是创建值的函数。

两者都可以。


0
投票

在 Dart 中,“只有 int、double、String、bool、null、List 或 Map(带有字符串键)类型的对象才能直接编码为 JSON。List 和 Map 对象是递归编码的”,请参阅 dart:convert

就目前情况而言,您的程序将仅适用于这些原始类型(请参阅下面的终端输出)。正如上面 lrn 所说,你必须指示你的程序如何解码特定类型。

这是一个代码示例,可以复制/粘贴并在 dartpad 中试用。

class AddressModel {
  final String street;
  final String city;
  // ... more properties
  AddressModel({
    required this.street,
    required this.city,
    /* More properties here */
  });
  factory AddressModel.fromJson(Map<String, dynamic> json) {
    return AddressModel(street: json['street'], city: json['city']);
  }

  @override
  String toString() {
    return '$runtimeType(street: $street, city: $city)';
  }
}

class SampleModel {
  final String firstName;
  final String lastName;
  final AddressModel address;
  SampleModel(
      {required this.firstName, required this.lastName, required this.address});

  factory SampleModel.fromJson(Map<String, dynamic> json) {
    return SampleModel(
        firstName: json['firstName'],
        lastName: json['lastName'],
        address: AddressModel.fromJson(json['address']));
  }

  @override
  String toString() {
    return '$runtimeType(firstName: $firstName, lastName: $lastName, address: $address)';
  }
}

typedef JsonDecoder<T> = T? Function(Map<String, dynamic> json);

class BaseResponse<T> {
  final bool error;
  final String? message;
  final T? result;

  const BaseResponse({required this.error, this.message, this.result});
  
  factory BaseResponse.fromJson(Map<String, dynamic> json, {JsonDecoder<T>? jsonDecoder}) {
    bool error = json['error'] != false;
    String? message = json['message'];
 
    return (jsonDecoder == null)? BaseResponse(
            error: error, message: message, result: json['result'] as T?): 
    BaseResponse(
            error: error, message: message, result: jsonDecoder(json['result']));
   }

  @override
  String toString() {
    return '$runtimeType(result: $result)';
  }
}

void main() {
  final jsonString = {'error': false, 'message': 'Hello', 'result': 'A simple String.'};
  final jsonListOfString = {'error': false, 'message': 'Hello', 'result': ['A simple String.']};
  final jsonAddress = {
    'error': false,
    'message': 'Hello',
    'result': {'city': 'Oslo', 'street': 'Anderson'}
  };
  final jsonSampleModel = {
    'error': false,
    'message': 'Hello',
    'result': {
      'firstName': 'Johan',
      'lastName': 'Andersen',
      'address': {'city': 'Oslo', 'street': 'Dass Gate'}
    }
  };
  final BaseResponse<String> r0 = BaseResponse.fromJson(jsonString);
  final BaseResponse<List<String>> r1 = BaseResponse.fromJson(jsonListOfString);
  final BaseResponse<AddressModel> r2 = BaseResponse.fromJson(jsonAddress, jsonDecoder: (json) => AddressModel.fromJson(json));
  final BaseResponse<SampleModel> r3 = BaseResponse.fromJson(jsonSampleModel, jsonDecoder: (json) => SampleModel.fromJson(json));

  print(r0);
  print(r1);
  print(r2);
  print(r3);
}

以及运行生成的终端输出

main
:

BaseResponse<String>(result: A simple String.)
BaseResponse<List<String>>(result: [A simple String.])
BaseResponse<AddressModel>(result: AddressModel(street: Anderson, city: Oslo))
BaseResponse<SampleModel>(result: SampleModel(firstName: Johan, lastName: Andersen, address: AddressModel(street: Dass Gate, city: Oslo)))

附注:Dart 模式 在验证传入的 json 时是一个强大的工具。

© www.soinside.com 2019 - 2024. All rights reserved.