Go 读取 JSON 定义 XLSX 文件生成 Dart Model 类

Go 读取 JSON 定义 XLSX 文件生成 Dart Model 类

假设有 JSON 定义的 XLSX 文件,每个工作表定义了不同的 JSON 定义。

那要把这样的 JSON 定义转换成 Model 类(本文是 Dart 语言的 Model 类),该如何处理?

本文代码仓库:github.com/bettersun/j...

JSON 定义工作表1:

其中 userList 下的 user 仅用于描述列表元素,并不是实际存在的 JSON 项目。

JSON 定义工作表2:

处理思路

  1. 遍历工作表,有 "JSON定义" 关键字的工作表认为有 JSON 定义。
  2. "JSON定义" 的下一行开始读取 "JSON项目" "类型" "非空" "说明" 所在列。
  3. 逐行读取JSON项目的定义,保存到列表中。
  4. 以缩进作为层级关系,组装成 JSON 项目节点树。
  5. 去掉不必要的节点树。
    如工作表1图中 userList 下的 user 仅用于描述列表元素,并不是实际存在的 JSON 项目。
  6. 找出需要定义为 Model 类的 JSON 项目。
    如工作表1图中 的 user 、dept 、 location 和整个 JSON 都需要定义成 Model 类。
  7. 组装成 Model 类代码,输出到文件。

JSON项目节点

JSON 项目会组装成树,所以一开始 JSON 项目的定义就定义成树节点。

struct 成员:

struct 成员 说明
Level 层级。读取 XLSX 单元格时通过缩进来计算
Name JSON项目名
Comment JSON项目说明,用于注释
ClassName JSON项目类名 需要定义成类的项目的类名,用于列表的元素对象类的定义。
ClassComment JSON项目类说明 需要定义成类的项目的类说明,用于列表的元素对象类的定义。
NodeType 节点类型
NotNull 不可空
Children 子节点

代码:

go 复制代码
// ItemNode JSON项目节点
type ItemNode struct {
    Level        int         // 层级
    Name         string      // JSON项目名
    Comment      string      // JSON项目说明
    ClassName    string      // JSON项目类名(需要定义成类的项目的类名,用于列表的元素对象类的定义)
    ClassComment string      // JSON项目类说明(需要定义成类的项目的类说明,用于列表的元素对象类的定义)
    NodeType     NodeType    // 节点类型
    NotNull      bool        // 不可空
    Children     []*ItemNode // 子节点
}

读取 XLSX 文件

Go 读取 XLSX 文件使用 excelize

Excelize 简体字文档

相关函数

打开文件:

go 复制代码
f, err := excelize.OpenFile(config.FileName)

获取工作表名列表:

go 复制代码
f.GetSheetList()

查找工作表名:

go 复制代码
result, err := f.SearchSheet(v, config.KeywordResponse, true)

分割列名和行号:

go 复制代码
excelize.SplitCellName(v)

获取所有行的值:

css 复制代码
rows, err := f.GetRows(sheetName)

最后得到每个JSON项目定义的列表,层级关系用缩进来确认。

各 JSON 项目的节点类型,通过映射根据事先设置的关键字进行处理。

例如:

  • 类型是 列表,则节点类型为 TypeArray
  • 类型是 字符串,则节点类型为 TypeString

具体读取代码参考仓库。

转换成树

将 JSON 项目的列表转换成树。

  1. 定义一个根节点。 2循环 JSON 列表。
  2. 如果 JSON 项目的 Level(等级)是父节点的 Level(等级)+ 1 ,那就将该JSON项目加入到父节点的子节点中。
  3. 如果当前元素的下一个元素的 Level (等级)大于当前元素的Level (等级),那下一个元素开始所有Level (等级)大于当前元素 Level (等级)的元素,递归调用当前处理。

主要代码:

go 复制代码
// 将JSON项目定义转换成树
func toTree(node *ItemNode, itemNodeList []*ItemNode) {
    nodeList := make([]*ItemNode, 0)

    for i := 0; i < len(itemNodeList); i++ {
       v := itemNodeList[i]

       if v.Level <= node.Level {
          break
       }

       // 当前元素的下一个元素的 level 更大,则下一个元素为当前元素的子
       if i < len(itemNodeList)-1 && itemNodeList[i+1].Level > v.Level {
          descendant := make([]*ItemNode, 0)
          for j := i + 1; j < len(itemNodeList); j++ {
             v2 := itemNodeList[j]
             if v2.Level > v.Level {
                descendant = append(descendant, v2)
             }
             if v2.Level <= v.Level {
                break
             }
          }

          toTree(v, descendant)
       }
    }

    for i := 0; i < len(itemNodeList); i++ {
       v := itemNodeList[i]

       // 元素的 level 为 参数节点的 leve +1 时,则添加到参数节点的子中
       if v.Level == node.Level+1 {
          nodeList = append(nodeList, v)
       }

       if v.Level < node.Level {
          break
       }
    }

    node.Children = nodeList
}

得到的 JSON 项目树如下(部分):

去掉 JSON 项目树中实际不存在的节点

如工作表1,其中 userList 下的 user 仅用于描述列表元素,并不是实际存在的 JSON 项目。

这时 userList 的 ClassName 需要设置成 user 的 Name ,然后 ClassComment 需要设置成 user 的 Comment 。

主要处理逻辑:

  • 当前节点是列表,且子节点是对象,则将子节点的所有子节点(当前节点的孙节点)放到当前节点的子节点中。
  • 递归处理当前节点的所有子节点。

主要代码:

go 复制代码
// 去掉多余的中间节点
func grandToChild(node *ItemNode) {
    for _, child := range node.Children {

       // 当前节点是列表,且子节点是对象,则将子节点的所有子节点(当前节点的孙节点)放到当前节点的子节点中。
       if node.NodeType == TypeArray && child.NodeType == TypeObject {
          node.ClassName = child.Name
          node.ClassComment = child.Comment
          var children []*ItemNode
          for _, grandChild := range child.Children {
             grandChild.Level = child.Level
             children = append(children, grandChild)
          }
          node.Children = children
       }
       grandToChild(child)
    }
}

得到的树如下(部分):

可以看到 user 节点已经不存在了,userList 的 ClassName 和 ClassComment 也变成了 原来 user 的 Name 和 Comment 。

整理出所有需要定义成类的 JSON 项目

由最初的定义可以得到,需要定义成类的 JSON 项目有下面几个。

  • 整个 JSON 定义
  • user 用户信息
  • dept 部门
  • location 位置

也就是根节点和所有节点类型是 数组 或 对象的 JSON 项目。

方法用了一个 JSON 项目,把所有需要定义成类的 JSON 项目添加到了该节点的子节点中。

go 复制代码
// 所有需要定义成类的对象
func allObject(node *ItemNode, objectNode *ItemNode) {
    objectNode.Children = append(objectNode.Children, node)
    for _, v := range node.Children {
       // 列表或对象时递归调用
       if v.NodeType == TypeArray {
          allObject(v, objectNode)
       }
       if v.NodeType == TypeObject {
          allObject(v, objectNode)
       }
    }
}

得到4个需要定义成类的 JSON 项目:

转换成 Dart Model 类

Dart Model 类定义需要的内容都有了,接下来就是组装字符串了,具体可参考代码,不再赘述。

本例代码中,

生成的根节点类名暂时使用了 root + 工作表下标,如 root0,root1。

生成的各 Model 类是添加了 json_serializable 的类。

需要使用相关命令再生成 xxx.g.dart 文件,即可消除编译错误。

文件名使用了 JSON 项目的 Name 或 ClassName 的蛇形(下划线连接小写字母)。

注: 输出到文件时,不存在的目录需要先创建。

生成的目录及文件:

root0.dart

dart 复制代码
import 'package:json_annotation/json_annotation.dart';
import 'model.dart';

part 'root0.g.dart';

///  
@JsonSerializable()
class Root0{
  /// 用户信息列表
  final List<User> userList;
  /// 部门
  final Dept dept;
  /// 位置
  final Location? location;

  Root0({
    required this.userList,
    required this.dept,
    this.location,
  });

  factory Root0.fromJson(Map<String, dynamic> json) => _$Root0FromJson(json);

  Map<String, dynamic> toJson() => _$Root0ToJson(this);
}

user.dart

dart 复制代码
import 'package:json_annotation/json_annotation.dart';
import 'model.dart';

part 'user.g.dart';

/// 用户信息 
@JsonSerializable()
class User{
  /// 用户ID
  final int userId;
  /// 用户名
  final String name;
  /// 状态
  final bool status;
  /// 性别
  final int? sex;
  /// 年龄
  final int? age;
  /// 地址
  final String? address;

  User({
    required this.userId,
    required this.name,
    required this.status,
    this.sex,
    this.age,
    this.address,
  });

  factory User.fromJson(Map<String, dynamic> json) => _$UserFromJson(json);

  Map<String, dynamic> toJson() => _$UserToJson(this);
}

结束语

难点就在将 JSON 项目列表转换成树的处理。

一开始各种乱试,总是不成功。

后来又用笔纸理了一下逻辑,才算弄清楚。

哎, 工作这么多年,连个树都写不好,着实有些丧了。


相关推荐
Flutter社区6 小时前
使用 Flutter 3.19 更高效地开发
flutter·dart
Forever不止如此8 小时前
【CustomPainter】绘制圆环
flutter·custompainter·圆环
wills7779 小时前
Flutter Error: Type ‘UnmodifiableUint8ListView‘ not found
flutter
AiFlutter1 天前
Flutter之Package教程
flutter
Mingyueyixi1 天前
Flutter Spacer引发的The ParentDataWidget Expanded(flex: 1) 惨案
前端·flutter
crasowas2 天前
Flutter问题记录 - 适配Xcode 16和iOS 18
flutter·ios·xcode
老田低代码3 天前
Dart自从引入null check后写Flutter App总有一种难受的感觉
前端·flutter
AiFlutter3 天前
Flutter Web首次加载时添加动画
前端·flutter
ZemanZhang4 天前
Flutter启动无法运行热重载
flutter