Json 视图化编辑工具

2,798 阅读8分钟

demo地址

pod 导入: pod 'PYJsonViewManager'

简介

描述

BaseJsonViewController是一个用OC编写的提供了搜索插入编辑查看路径复制json/value等功能的Json可视化编辑工具。

由于网络数据请求下来后,APP端对json原数据的展示并不明朗。 修改网络数据只能通过Charles等抓包工具实现,受到的限制太多,所以诞生了在APP端直接对json进行查看、修改的Json视图工具:BaseJsonViewController

后续会对BaseJsonViewController进行持续的更新优化,欢迎使用。

主要功能

json结构展示:

  1. 一键压缩/展开 : 点击👀all 展开全部,点击🦆…压缩全部(需要注意的是,如果进行了压缩,处在插入状态的cell,将被删除)。
  2. 添加了层级的背景色、缩进等。默认最大展示6个层级,如果超过6个层级则跳转到新的页面,进行展示。
  3. 对类型的区分:分为Dictionary Array String Number
  4. 支持展开与收起功能,如果有子节点,则单击可以展开\收起。
  5. value的展示:一行cell 的 value默认最多展示两行。如果超过两行则压缩,并在底部展示。

搜索功能:

点击放大镜可以进入搜索页面

在源码中的位置:BaseJsonViewController->BaseJsonViewMainView->BaseJsonHeaderView->BaseJsonViewSearchView

  1. 搜索关键词:输入关键词,并且会自动进行搜索。

  2. 精准搜索:如果选中精准搜索,搜索策略将从containsString 变成 isEqualToString

注意:不管是否为精准搜索,都区分大小写。

  1. 搜索Editing:如果选中【搜Editing】按钮,则会搜索整个json中处在Editing状态并符合关键词搜索的数据。

注意:如果有处在插入状态的数据,这时候会自动被删除。

  1. 上一个\下一个:当搜索完成后,点击【上一个】、【下一个】自动跳转到相应的行。

  2. 查看总览:跳转到一个搜索结果总览控制器,显示了搜索结果的路径及`value

  3. 展示路径/搜索数量:当没有搜索条件(即:没有搜索词 、且搜Editing处于非选中状态)时,显示的是本控制器节点的路径。否则显示的是搜索结果数量。

  4. 展示路径/报错信息:具有滚动、放大功能,最大放大倍数为1倍

  5. 当搜索条件报错时,展示的是红色的报错信息。

  6. 当有搜索内容时,展示的是当前选中的搜索结果的节点路径,

删除功能

侧滑cell,出现删除功能(注意,因为侧滑功能比较多,所以在iphone5上面会导致删除功能被遮挡)

复制功能

复制功能分为两种:

  1. 如果侧滑cell对应的节点为ArrayDictionary 则会只能复制 json
  2. 如果侧滑cell 对应的节点为StringNumber则可以复制jsonvalue

编辑功能

侧滑cell,并点击编辑按钮开启编辑功能(下面把被编辑的节点称为Model,把Model的父节点称为SuperModel)。

  1. SuperModel类型对Modelkey 的影响:

  2. SuperModel点为Array类型:Model的key必须为空。

  3. SuperModelDictionary类型:Model的key必须有值。

  4. 点击取消按钮:取消所有修改。

  5. 点击完成按钮:

  6. 选中Number按钮,转成Numbser类型,输入的值必须为数字,否则会报错,并在报错位置进行显示。

  7. 选中String按钮,转成String类型,会有个默认值,默认值为""

  8. 选中json按钮:

    1. 如果ModelArray类型,则会把jons解析出来作为Model的子节点数据
    2. 如果ModelDictionary类型,则会直接解析Json,如果json内包含一个对象则该对象作Model的数据,把对象的Key作为Modelkey
  9. 选中Dictionary按钮:

    1. 如果ModelDictionary类型,则不会产生任何效果,否则Model清空子节点数据,并把Model转成Dictionary类型。
  10. 选中Array按钮:

    如果ModelArray类型,则不会产生任何效果,否则Model清空子节点数据,并把Model转成Array类型。

插入功能

侧滑cell,并点击插入按钮开启编辑功能

注意:如果在插入节点没有点击完成的情况下,对节点父节点执行收起操作,会自动删除刚刚插入的节点

注意:如果插入节点父节点为Dictionary类型,插入的节点父节点中的顺序不固定

把被编辑的节点称为Model

Model的父节点称为SuperModel

Model插入的子节点称为SubModel

SuperModel插入的子节点称为SuperSubModel)。

  1. 如果ModelDictionary则可以【插入子节点】或【插入父节点】。
    1. 【插入子节点】:
      1. 如果Model关闭状态,则自动展开Model,并在Model字节点的第一行插入一个新的节点SubModel,这时候,SubModel处于被编辑状态
      2. 注意:此时插入的SubModel在父节点Model无序
    2. 【插入父节点】:在Model的后面插入为SuperSubModel插入SuperSubModel
  2. 如果ModelArray类型,则可以【插入子节点】或【插入父节点】。
    1. 【插入子节点】:
      1. 如果Model展开状态,则自动压缩Model,并在Model字节点的第一行插入一个新的节点SubModel,这时候,SubModel处于被编辑状态
      2. 注意:此时插入的SubModel在父节点Model有序
    2. 【插入父节点】:在Model的后面插入为SuperSubModel插入SuperSubModel
  3. 如果ModelStringNumber类型,则可以【插入父节点】。在Model的后面插入为SuperSubModel插入SuperSubModel

实现思路

  1. json的解析
    1. 为了避免造成不必要的开销,对json解析的时机做了调整:
      1. 当节点A被打开时候,才会解析A的子节点数据。
      2. 在解析节点A数据时,优先获取缓存的A子节点数据。
      3. 在对A进行编辑插入时,对A的的子节点数据进行更新。
  2. 对视图的展示
    1. 对与无限层级缩放的视图来说,我们有必要把数据展平。
    2. 数据中创建一个用于标记层级的变量。来做一个无限缩放层级的假象。

实现细节

对于节点Model的定义

Model就代表了一个节点,所以Model的结构至关重要。

主要的属性:
  1. level:所处层级,在进行初始化时,根据父节点的level进行赋值。
@property (nonatomic,assign) NSInteger level;
  1. count:字节点的个数
@property (nonatomic,assign) NSInteger count;
  1. isOpen是否为打开状态
@property (nonatomic,assign) BOOL isOpen;
  1. originData:所有子节点的原始数据(可能为nil、Array、Dictionary、Number、String)
@property (nonatomic,strong) id originData;
  1. key:如果originData为字典,则key就是originData的key,否则为nil
@property (nonatomic,strong) NSString *key;
  1. data: originData 转化成的数据(可能为:nil、NSString、 NSArray、BaseJsonViewStepModel)
@property (nonatomic,strong) id data;
  1. originData:父节点( 在父节点创建子节点时,进行的赋值)
@property (nonatomic,weak) BaseJsonViewStepModel *superPoint;
  1. type:当前节点的类型
typedef enum : NSUInteger {
    BaseJsonViewStepModelType_Dictionary,
    BaseJsonViewStepModelType_Array,
    BaseJsonViewStepModelType_Number,
    BaseJsonViewStepModelType_String,
} BaseJsonViewStepModelType;

@property (nonatomic,assign) BaseJsonViewStepModelType type;
  1. 所处的状态
typedef enum : NSUInteger {
    BaseJsonViewStepCellStatus_Normal,
    BaseJsonViewStepCellStatus_EditingSelf,
    BaseJsonViewStepCellStatus_InsertItem,
} BaseJsonViewStepCellStatus;

@property (nonatomic,assign) BaseJsonViewStepCellStatus status;
对model的创建
  1. + (BaseJsonViewStepModel *) createStepModelWithOriginData: (id) data andKey: (NSString *)key
/**
 创建 一个model

 @param data 原始的子节点数据
 @param key 创建出的model对应的key
 @return model
 */
+ (BaseJsonViewStepModel *) createStepModelWithOriginData: (id) data andKey: (NSString *)key{
    BaseJsonViewStepModel *model = [BaseJsonViewStepModel new];
    model.originData = data;
    model.key = key;
    return model;
}	
  1. + (BaseJsonViewStepModel *(^)(id)) createWithID

类方法,返回一个blockblock 传入的是id类型的数据。数据可以是

  1. BaseJsonViewStepModel: 直接返回这个data。不再创建
  2. NSString:先转成字典,然后创建model

使用方法 BaseJsonViewStepModel.createWithId(data);

+ (BaseJsonViewStepModel *(^)(id)) createWithID {
    return ^(id data) {
        BaseJsonViewStepModel *model;
        if ([data isKindOfClass:BaseJsonViewStepModel.class]) {
            model = data;
        }
        if ([data isKindOfClass:NSString.class]) {
            NSDictionary *dic = BaseJsonViewManager.convertToDicWithJson(data);
            if (dic) {
                model = BaseJsonViewManager.convertToStepModelWithDic(dic);
            }
        }
        if (!model) {
            model = [BaseJsonViewStepModel createStepModelWithOriginData:data andKey:@""];
        }
        return model;
    };
}
搜索功能

搜索功能将会搜索出 所有的符合条件的model,并返回一个数组

isSearchEditing的筛选策略

  1. isSearchEditing:如果为true。
    1. 如果keynil,则搜索全部处在编辑状态的model。
    2. 如果key有值
      1. 如果isAccurateSearch为true:搜索所有keyvalue isEqualToString key的正在编辑状态的model
      2. 如果isAccurateSearch为true:搜索所有keyvalue containsString key的正在编辑状态的model
/**
 搜索

 @param key 搜索 关键字
 @param isAccurateSearch 是否为精准搜索(如果选中精准搜索,搜索策略将从`containsString` 变成 `isEqualToString`。不管是否为精准搜索,都区分大小写)
 @param isSearchEditing 是否搜索正在编辑状态的model
 @return 搜索结果
 */
- (NSMutableArray <BaseJsonViewStepModel *>*) searchWithKey:(NSString *)key andIsAccurateSearch: (BOOL) isAccurateSearch andIsSearchEditing:(BOOL) isSearchEditing {
    SBaseJsonViewStepSearchModelConfig config;
    config.isSearchEditing = isSearchEditing;
    config.isAccurateSearch = isAccurateSearch;
    config.key = key;
    config.model = self;
    return BaseJsonViewStepSearchModel.getResultWithSearchConfig(config);
}
删除功能

从父节点移除本节点

这个功能主要是找到originData中相同的元素,进行删除。

- (void) removeFromeSuper {
  
    if ([self.superPoint.originData isKindOfClass:NSArray.class]) {
        NSArray *array = self.superPoint.originData;
        NSMutableArray *arrayM = [[NSMutableArray alloc]initWithArray:array];
        [arrayM removeObject:self.originData];
        self.superPoint.originData = arrayM;
    }
    if ([self.superPoint.originData isKindOfClass:NSDictionary.class]) {
        NSDictionary *dic = self.superPoint.originData;
        NSMutableDictionary *dicM = [[NSMutableDictionary alloc]initWithDictionary:dic];
        NSString *key = self.key;
        if (key.length > 0) {
            dicM[self.key] = nil;
        }
        self.superPoint.originData = dicM;
    }
    
    [self.superPoint reloadDataWitOriginDataProperty];
}
插入节点

根据原始数据插入节点,并返回BaseJsonViewStepErrorModel

BaseJsonViewStepErrorModel 记录了插入时的错误信息

/**
 插入一个节点

 @param key 节点的key
 @param originData 节点的原始子节点y数据
 @param index 插入的位置
 @return 插入报错的model
 */
- (BaseJsonViewStepErrorModel *) insertWithKey: (NSString *)key
         andOriginData: (id) originData
              andIndex:(NSInteger) index;

根据model插入节点,并返回BaseJsonViewStepErrorModel

BaseJsonViewStepErrorModel 记录了插入时的错误信息



/**
 插入一个Model

 @param model 准备插入的 节点 model
 @param index 插入的位置
 @return 错误信息
 */
- (BaseJsonViewStepErrorModel *) insertWithModel: (BaseJsonViewStepModel *) model
                                        andIndex:(NSInteger) index;

最后

截图:

彩蛋:点击title会复制当前Controller展示的json数据呦~

工具刚刚成型,很多需要修改的地方,希望大家勇于提bug 谢谢~

demo地址

pod 导入: pod 'PYJsonViewManager'