准备
安装
react-json-view
:
npm install --save react-json-view
可参考的一些开源库:
react-json-path-picker
,
json-path-picker
线上工具:
jsonpath tool
JsonPath
用来解析多层嵌套的json数据。JsonPath 是一种信息抽取类库,是从JSON文档中抽取指定信息的工具,提供多种语言实现版本,包括:Javascript, Python, PHP 和 Java。
本质: 通过一种语法规则
快速从 JSON 数据中提取数据
。类似于 正则表达式 通过一定规则从 text 文本内容提取数据。
语法规则:
参考文档:
JSON 数据提取
实现
import ReactJson from 'react-json-view';
interface Props {
jsonValue: any;
updateJsonPath: (value: string) => void;
}
export function JsonPathFilter(props: Props) {
const { jsonValue, updateJsonPath } = props;
const getValue = (select: any) => {
const { name, namespace } = select;
namespace.push(name);
const reg = /^[0-9]*$/g;
let path = '';
namespace.forEach((s: string) => {
if (reg.test(s)) {
path = path?.substring(0, path.length - 1);
path = `${path}[${s}].`;
} else {
path = `${path + s}.`;
}
});
path = path?.substring(0, path.length - 1);
return path;
};
return <ReactJson src={jsonValue} onSelect={(select) => updateJsonPath(getValue(select))} />;
}
select
数据类型:
效果图:
进一步完善
考虑到上述方案只能通过点击叶子结点的value来获取path,如果说我们点击
任意节点的key或者value都可以获取到path的话
,上述方案就不太可行,
react-json-view
开源库不提供对任意节点点击的监听事件。
调研发现,我们可以使用
jsoneditor
,注意官方给定的
onSelectionChang
方法,是针对选择多行(或者一行)时,起始节点数据,比如下图所示,选择的是一个区间。这种用法并不太符合我们的要求,我们希望的是对节点的点击事件进行监听。
查看文档,找到了
onEvent
方法,可以对节点的key和value进行事件监听,包括鼠标移入移除,点击,聚焦等事件。
这里,我们需要监听的是点击事件。
首先,我们先封装一下JsonEditor,代码如下,
JsonEditor/index.tsx:
import { useMemoizedFn } from 'ahooks';
import JSONEditor from 'jsoneditor';
import { useEffect, useState } from 'react';
import './index.less';
interface Props {
jsonValue: any;
otherOpts?: any;
domId?: string;
isExpandAll?: boolean;
containerStyle?: any;
}
export default function JsonEditor(props: Props) {
const { jsonValue, otherOpts = {}, domId = 'jsoneditor', isExpandAll, containerStyle } = props;
const [jsonRef, setJsonRef] = useState<any>(null);
let editor: any;
const renderJsonEditor = useMemoizedFn(() => {
const container = document.getElementById(domId);
if (container) {
container.innerHTML = ''; // 防止添加多个jsoneditor
const options: any = {
mode: 'view',
modes: ['code', 'text', 'tree', 'view'],
language: 'en',
...otherOpts,
};
editor = new JSONEditor(container, options);
editor.set(jsonValue);
if (isExpandAll) {
editor.expandAll();
}
}
});
useEffect(() => {
renderJsonEditor();
return () => {
editor?.destroy();
};
}, [jsonRef, renderJsonEditor, jsonValue, editor]);
return <div style={containerStyle} ref={setJsonRef} id={domId} />;
}
JsonEditor/index.less:
div.jsoneditor-field,
div.jsoneditor-value {
cursor: pointer;
}
调整后的JsonPathFilter代码如下,
JsonPathFilter/index.tsx:
import JsonEditor from '../JsonEditor';
interface Props {
jsonValue: any;
updateJsonPath: (value: string) => void;
domId: string;
}
export function JsonPathFilter(props: Props) {
const { jsonValue, updateJsonPath, domId } = props;
const getValue = (namespace: any) => {
let path = '';
namespace?.forEach((s: string | number) => {
if (typeof s === 'number') {
path = path?.substring(0, path.length - 1);
path = `${path}[${s}].`;
} else {
path = `${path + s}.`;
}
});
path = path?.substring(0, path.length - 1);
return path;
};
return (
<JsonEditor
jsonValue={jsonValue}
domId={domId}
otherOpts={{
onEvent: (item: any, event: any) => {
if (event.type === 'click') { // 判断事件类型
// 监听点击 key 或者 value 的时候
updateJsonPath(getValue(item.path));
}
},
}}
/>
);
}