POJO可视化编辑方案,包含POJO到JsonSchema的解析器.Automatically generate JsonSchema according to the class information of the POJO
APACHE-2.0 License
English | 简体中文
POJO编辑器可以根据POJO的Class信息自动生成JosnSchema,搭配依据JsonSchema的可视化编辑工具轻松达到对POJO进行可视化,结构化,带有约束与校验的创建或编辑.
这可以使得非开发人员在编辑一些POJO对象描述的配置或者原始数据时更加直观高效,并能在一定程度上减少发生错误的情况.
以下示例包含了编辑器支持的大部分类型
https://mochi-c.github.io/POJOEditor/
该示例所对应的pojo如下
public class ExampleClass {
String autoField;
//名称提示等
@JsonEditorUIMeta(title = "myTitle", desc = "myDesc", guide = "myGuide")
String titleDesGuide;
@JsonEditorUIMeta
String string;
@JsonEditorUIMeta(format = JsonEditorFormat.TEXT_AREA)
String textarea;
@JsonEditorUIMeta(format = JsonEditorFormat.TIME_SELECTOR)
@JsonEditorDateTimeSelector(resultFormat = "m-d H:i:S")
String timeWithSpecialResult;
@JsonEditorUIMeta(format = JsonEditorFormat.DATE_SELECTOR)
String date;
@JsonEditorUIMeta
int integer;
@JsonEditorUIMeta
double number;
@JsonEditorUIMeta
SimpleEnum simpleEnumSelection;
@JsonEditorUIMeta(format = JsonEditorFormat.SELECT)
@JsonEditorEnumBuilder(itemsBuilder = DynamicEnum.class)
String dynamicEnumSelection;
@JsonEditorUIMeta(format = JsonEditorFormat.TAGS)
List<SimpleEnum> simpleEnumTags;
@JsonEditorUIMeta(format = JsonEditorFormat.TAGS)
@JsonEditorEnumBuilder(itemsBuilder = DynamicEnum.class)
List<String> dynamicEnumTags;
@JsonEditorUIMeta
SimpleClass simpleClass;
@JsonEditorUIMeta(format = JsonEditorFormat.TABLE)
List<SimpleClass> table;
@JsonEditorUIMeta(format = JsonEditorFormat.TABS)
@JsonEditorArray(titleTemplate = "myName A:{{self.objA}} B:{{self.objB}}")
List<SimpleClass> specialNameTabs;
@JsonEditorUIMeta
List<SimpleClass> array;
@JsonEditorUIMeta(format = JsonEditorFormat.TABLE)
@JsonEditorArray(itemFormat = JsonEditorFormat.SELECT) //Can be omitted
@JsonEditorEnumBuilder(itemsBuilder = DynamicEnum.class)
List<String> arrayStringToEnum;
@JsonEditorUIMeta(format = JsonEditorFormat.TABLE)
@JsonEditorArray(itemFormat = JsonEditorFormat.TIME_SELECTOR) //Can be omitted
@JsonEditorDateTimeSelector //Can be omitted
List<String> arrayStringToTime;
public static class DynamicEnum implements IEnumItemBuilder {
@Override
public Map<String, String> getItems() {
Map<String, String> items = Maps.newHashMap();
items.put("Now", "Now");
items.put("TIME", System.currentTimeMillis() + "");
items.put("NAME", "SPECIALNAME");
return items;
}
}
public static enum SimpleEnum {
enumA,
enumB,
@JsonEditorUIMeta(title = "SpecialName")
enumC
}
public static class SimpleClass {
@JsonEditorUIMeta
String objA;
@JsonEditorUIMeta
String objB;
}
}
编辑器由schemaParser和jsonEditor组合而成, 两者分别部署在服务之中,以下为一个典型的配置编辑流程参考
后端服务从配置中心加载配置解析为pojo供上层使用
后端解析pojo对应class的schema并由前端页面生成控件
后端将该pojo实例json序列化并在编辑页面加载
编辑页面完成修改由控件生成json数据回传到后端服务
后端服务根据json数据反序列化得到新的配置信息并更新配置中心
用于java端解析pojo格式生成jsonSchema,配合注解和过滤器可以实现field的过滤和交互方式的指定
<dependency>
<groupId>com.mochi-cell.tools</groupId>
<artifactId>jsoneditorschemaparser</artifactId>
<version>0.1.3</version>
</dependency>
JsonEditorParserBuilder.create(ExampleClass.class).parse();
JsonEditorParserBuilder.create(ExampleClass.class).setFieldFilters(Lists.newArrayList(SIMPLE_FIELD_FILTER.EVERY_FIELD)).addFormatDictionary(String.class, JsonEditorFormat.TEXT_AREA).parse();
提供可视化编辑的js控件,使用了开源的json-editor,部分交互形式需要搭配selectize和flatpickr插件
npm
npm install @json-editor/json-editor
cdn
<script src="https://cdn.jsdelivr.net/npm/@json-editor/json-editor@latest/dist/jsoneditor.min.js"></script>
const element = document.getElementById('editor_holder');
const editor = new JSONEditor(element, options);
https://github.com/json-editor/json-editor
JsonEditorUIMeta注解为field或class提供一些基本信息的配置,在ONLY_ANNOTATION的过滤器中也用作参与编辑的标志,对于没有标明该注解的成员,参与解析时使用默认值
成员 | 含义 | 默认值 |
---|---|---|
guide | 元素名称边上的 ? 提示,鼠标悬停上去会展开展示 | null 不显示提示 |
desc | 描述信息,通常出现在编辑框下方 | null 不显示描述 |
format | 用来指明需要使用的交互类型 | AUTO 根据类型自动转换 |
title | 该元素的别名 | null 使用成员的命名 |
JsonEditorDependence注解可以会为json schema 追加 dependence option. 用以控制该 field 是否生效. 该注解是一个多重注解,多个条件需要同时生效,该field才会参与编辑,不参与编辑field不被显示,控件生成的json数据也不包含.
成员 | 含义 |
---|---|
dependenceKey | 作为激活条件的field的name |
dependenceValue | 作为激活条件的field的值 |
class Test {
@JsonEditorUIMeta
@JsonEditorDependence(dependenceKey = "simpleEnumSelection", dependenceValue = "enumA")
@JsonEditorDependence(dependenceKey = "dynamicEnumSelection", dependenceValue = "Now")
String dependenceItem;
@JsonEditorUIMeta
SimpleEnum simpleEnumSelection;
@JsonEditorUIMeta(format = JsonEditorFormat.SELECT)
@JsonEditorEnumBuilder(itemsBuilder = DynamicEnum.class)
String dynamicEnumSelection;
}
上述例子中,只有当 simpleEnumSelection 为 enumA 且 dynamicEnumSelection 为 now 的时候 dependenceItem 才会生效,参与编辑.
对于各个字段的交互形式,可以通过JsonEditorUIMeta中的format来显示的指定.对于没有显示的指定的,解析器可以根据field的class自动转换,自动转换默认支持java的各基本类型包装类和容器,同时使用者也可以在配置中自己添加class到format的映射,默认的映射规则也是可以被覆盖的.
对于一些特殊的FORMAT存在对field的class限制,因为要从中获取一些必要的信息.还有一些format提供了一定的扩展功能,可以通过使用追加的注解来实现
前端页面编辑器中生成的json数据各个成员的类型与format是相对应的,对于一些特殊的类型,使用者需要自己处理前端页面编辑器得到的json数据到pojo的转换
类型 | 效果 | Json类型 | 类型限制 | 扩展 |
---|---|---|---|---|
STRING | 简单的文本框 | String | × | × |
TEXT_AREA | 可调整大小的文本框,适合较多的文字内容 | STRING | × | × |
TIME_SELECTOR | 时间选择器 | STRING | × | √ |
DATE_SELECTOR | 日期选择器 | STRING | × | √ |
INTEGER | 整数 | NUMBER | × | × |
NUMBER | 数字 | NUMBER | × | × |
BOOLEAN | True,False下拉选择 | BOOLEAN | × | × |
BOOLEAN_CHECK_BOX | 勾选框 | BOOLEAN | × | × |
ARRAY | 完全展开的数组 | ARRAY | √ | √ |
TABLE | 使用统一的表头展开的表格类型的数组 | ARRAY | √ | √ |
TABS | 一次查看一个元素,重复使用视图的TAB页类型数组,页签在左侧 | ARRAY | √ | √ |
TABS_TOP | 一次查看一个元素,重复使用视图的TAB页类型数组,页签在顶部 | ARRAY | √ | √ |
SELECT | 下拉选择框,可以直接输入进行快捷搜索 | STRING | √ | √ |
TAGS | 标签选择器 | ARRAY[STRING] | √ | √ |
OBJECT | 递归解析Class对象,一般由解析器自动调用 | OBJECT | - | - |
AUTO | 根据默认类型转换自动选择 | - | - | - |
JAVA类型 | 默认FORMAT类型 |
---|---|
String | STRING |
byte,short,int,long,及其包装类 | INTEGER |
float,double,及其包装类 | NUMBER |
boolean,Boolean | BOOLEAN |
List/Set | ARRAY |
enum | SELECTOR |
List | TAGS |
JsonEditorParserBuilder.create(ExampleClass.class).addFormatDictionary(String.class, JsonEditorFormat.TEXT_AREA);
对于ARRAY,TABLE,TABS,TABS_TOP系列的ARRAY类型,要求对应的field一定是array,list,set,collection,因为解析器需要查询泛型的信息来递归解析array内部的信息
对于TAGS,要求对应的field一定是List<Enum>或者List<String>,对于List<String>还需要配合@ConfigEditorEnumBuilder注解提供枚举项
对ARRAY,TABLE,TABS,TABS_TOP系列的数组类型提供扩展
成员 | 含义 |
---|---|
titleItem | 当List嵌套object时,使用对应object中的成员名作为List元素的别名,titleItem指明object中的成员名 |
showIndex | List元素的的名称中标明该元素在List中的下标,默认为true |
titleTemplate | 当List嵌套object时,List元素的别名的模板,其中i代表下标,self为list中的当前元素, 示例: {{i}} - {{self.xxx}} + {{self.bbb.aaa}} |
itemFormat | 强制指定List中元素的交互类型 |
对TIME_SELECTOR,DATE_SELECTOR,时间/日期选择器提供扩展
成员 | 含义 |
---|---|
resultFormat | 生成数据时的时间格式, 默认为 Y-m-d H:i:S |
在SELECT,TAGS一类需要枚举的FORMAT中将String类型转换为枚举,使用该注解能够实现运行时动态的生成一枚举选项
成员 | 含义 |
---|---|
itemsBuilder | 生成枚举项的接口,返回一个Map<String,String> key为枚举的值,value为选择器中显示的别名 |
对于Array系列的Format可以可以直接使用扩展注解可以改变泛型的类型例如使用JsonEditorDateTimeSelector将list元素内转换为时间选择器,使用JsonEditorEnumBuilder将元素转换为枚举.
对于SELECT,TAGS一类需要枚举的FORMAT中,对于Enum类型可以直接解析.选择框中支持别名,Enum通过在枚举值中使用ConfigEditorUIMeta中的title定义别名,String依靠注解中的构造器指明别名.编辑器中将显示别名,但得到的json数据将是原值
通过FIELD过滤器可以对class内的field进行过滤,用以确认哪些field是参与编辑的,编辑器自带了两个过滤器,其中SIMPLE_FIELD_FILTER是默认的
SIMPLE_FIELD_FILTER.SIMPLE_FIELD_FILTER 所有添加了@JsonEditorUIMeta注解的field参与编辑,其余的不参与 SIMPLE_FIELD_FILTER.EVERY_FIELD 所有成员都参与编辑
同时可以设置多个过滤器,使用者也可以自己按需要追加或者重置过滤器
存在多个过滤器时,所有的过滤器链式工作,全部通过的field才会参与编辑
static class MyField implements IFieldFilter {
@Override
public boolean match(Field field) {
/**
* 该field是否生效,参与编辑
* true 生效
* false 不生效
*/
return true;
}
}
public static void main(String[] args) {
//重置过滤器
JsonEditorParserBuilder.create(ExampleClass.class).setFieldFilters(Lists.newArrayList(new MyField()));
//追加过滤器
JsonEditorParserBuilder.create(ExampleClass.class).addFieldFilter(new MyField());
}