Generic badge Generic badge Pub Package pub points

jsontree

静态类型的JSON树。

该树只能包含JSON兼容的原子类型,而不能包含其他任何内容。也就是说,只有Stringintdoubleboolnull — 通过嵌套的MapList对象组合而成。

这允许您在非常早期阶段预防数据错误。您将看到IDE的警告,并且程序将无法编译。

示例

以声明式风格创建树

import 'package:jsontree/jsontree.dart';

void main() {
  final tree = {
    "planet": "Mars".jsonNode,
    "diameter": 6779.jsonNode,
    "satellites": ["Phobos".jsonNode, "Deimos".jsonNode].jsonNode
  }.jsonNode;

  print(tree.toJsonCode());
  // {"planet":"Mars","diameter":6779,"satellites":["Phobos","Deimos"]}
}

或者以命令式风格创建树

import 'package:jsontree/jsontree.dart';

void main() {
  final satellites = MutableJsonList.empty();
  satellites.data.add("Phobos".jsonNode);
  satellites.data.add("Deimos".jsonNode);

  final tree = MutableJsonMap.empty();
  tree.data["planet"] = "Mars".jsonNode;
  tree.data["diameter"] = 6779.jsonNode;
  tree.data["satellites"] = satellites;

  print(tree.toJsonCode());
  // {"planet":"Mars","diameter":6779,"satellites":["Phobos","Deimos"]}
}

动机

设想我们需要创建一个JSON request,稍后它将被转换为JSON并发送到服务器。

错误:动态类型

import 'dart:convert';

main() {
  final request = <String, dynamic>{};  // to be converted to JSON

  // DateTime is not convertible, but we don't know that yet
  request["time"] = DateTime.now();  // oops  
  request["message"] = "Hi!";

  // runtime exception: DateTime cannot be converted
  send(json.convert(request));
}

正确:静态类型

import 'dart:convert';
import 'package:jsontree/jsontree.dart';

respond() {
  final request = MutableJsonMap();  // no dynamic types

  // to place an object inside MutableJsonMap we are forced to convert each 
  // parameter to a JsonNode. But there's no way to convert DateTime to it,
  // so we have to do it right
  request["time"] = DateTime.now().millisecondsSinceEpoch.jsonNode;  
  request["message"] = "Hi!".jsonNode;

  // no errors, as it should be
  send(json.convert(request));
}

JsonNode树的创建

x.jsonNode会创建一个包装x值的对象。该对象取决于x的类型。

例如,5.jsonNode会创建JsonInt(5)。而5.23.jsonNode会创建JsonDouble(5.23)

这同样适用于集合。

final sheldon = {
    'name': 'Sheldon'.jsonNode,
    'surname': 'Cooper'.jsonNode,
    'iq': 187.jsonNode,
    'girlfriends': 1.jsonNode
}.jsonNode; 

// you can't add .jsonNode to the map if you miss at least 
// one .jsonNode added to elements

final leonard = {
    'name': 'Leonard'.jsonNode,
    'surname': 'Hofstadter'.jsonNode,
    'iq': 173.jsonNode,
    'girlfriends': 4.jsonNode
}.jsonNode;

// connect these nodes into an even larger structure

final tree = {
    'science': 'physics'.jsonNode,
    'neighbours': [leonard, sheldon].jsonNode
}.jsonNode; 

无论类型如何,所有包装器对象都将继承自基类JsonNode。如果您已经创建了一个JsonNode,您可以确信其中包含JSON兼容的数据。

JsonNode树转换为JSON字符串

对于任何JsonNode对象,您都可以调用.toJsonCode()方法将其转换为JSON字符串。

import 'package:jsontree/jsontree.dart';
...

final tree = [1.jsonNode, 2.jsonNode].jsonNode;
print(tree.toJsonCode());

您也可以将树直接传递给json.convert

import 'package:jsontree/jsontree.dart';
import 'dart:convert';
...

final tree = [1.jsonNode, 2.jsonNode].jsonNode;
print(json.convert(tree));

JSON字符串转换为JsonNode树

使用此库解析JSON只有在您想使用解析后的值来创建另一个树时才有意义。

final a = JsonNode.fromJsonCode(src1);
final b = JsonNode.fromJsonCode(src2);

print([a, b, "something else".jsonNode].jsonNode.toJsonCode())

JsonNode树转换为原始对象

您还可以调用JsonNode.unwrap()来移除所有包装器,并获得原始的Dart对象集合。因为这些对象在创建树时已经过验证,所以结果保证可以转换为JSON。

import 'package:jsontree/jsontree.dart';
import 'dart:convert'
...

JsonList tree = [1.jsonNode, 2.jsonNode].jsonNode;
List<int> list = tree.unwrap();  // [1, 2]

// of course, the list convertible to JSON 
print(json.convert(dartList));

对象转换为JsonNode树

这种转换违背了该库的宗旨。它需要动态类型检查,并可能导致运行时错误。

但是,如果您已经准备好了数据结构,这可能是一个合理的折衷。

final leonard = {
    'name': 'Leonard',
    'surname': 'Hofstadter',
    'iq': 173,
};

JsonNode tree = JsonNode.wrap(leonard);

JsonNodes的不可变性

默认情况下,所有对象都是不可变的。

JsonMap m = {"a": 1.jsonNode, "b": 2.jsonNode}.jsonNode;
// you can read m or m.data, but cannot change 

列表和映射也有可变版本。

var m = MutableJsonMap({"a": 1.jsonNode, "b": 2.jsonNode});
// you can read/write m and m.data 

可变性和不可变性可以在对象创建后实现。

JsonMap readOnly = {"a": 1.jsonNode, "b": 2.jsonNode}.jsonNode;

MutableJsonMap readWrite = readOnly.toMutable();  // creates a copy
readWrite["c"] = 3.jsonNode;

JsonMap readOnlyAgain = readWrite.asImmutable();  // wraps the data as immutable

toMutable将创建数据的副本,同时尊重原始对象的不可变性。

asImmutable将只是将数据包装在一个不允许修改的对象中。

层级结构

JsonAny
^^ JsonValue
   ^^ JsonInt
      ^^ JsonInt53   (JavaScript range)
      ^^ JsonInt64   (full int64 range) 
   ^^ JsonDouble
   ^^ JsonString
^^ JsonList
   ^^ MutableJsonList
^^ JsonMap
   ^^ MutableJsonMap
^^ JsonNull

默认情况下,除了MutableJsonMapMutableJsonList之外,所有对象都是不可变的。

整数范围

默认情况下,int.jsonNode会创建一个JsonInt53对象。它只允许您设置在JavaScript中不会丢失精度的整数值。

final a = 5.jsonNode;  // no problem
final b = 9999999999999999.jsonNode;  // throws ArgumentError

此限制很重要,因为JSON字面意思是JavaScript对象表示法。

但是大多数语言都能从JSON中读取更大的数字。要存储完整范围的数字,请使用int.jsonNode64

final c = 9999999999999999.jsonNode64;  // no problem

许可证

版权所有 © 2022 Artёm IG。根据MIT许可证发布。

GitHub

查看 Github