recogn-parse
Version:
recogn-parse 提供了 识别--解析 的机制,使用者只需要添加识别器 和 解析器,然后就可以对事件进行识别和解析,适合用所有具备 识别--解析 流程的场景中,如:加载并显示各种模型、预览文件等
334 lines (246 loc) • 11.1 kB
Markdown
[API接口文档]: ./api/index.md
[设计与机制]: ./设计与机制.md
[对鉴定者列表执行识别]: ./assets/对鉴定者列表执行识别.png
[执行串行鉴定者]: ./assets/执行串行鉴定者.png
[执行串行解析器]: ./assets/执行串行解析器.png
[识别]: ./assets/识别.png
[执行鉴定者]: ./assets/执行鉴定者.png
[解决]: ./assets/解决.png
**目录:**
<!-- @import "[TOC]" {cmd="toc" depthFrom=1 depthTo=6 orderedList=true} -->
<!-- code_chunk_output -->
1. [入门](#入门)
2. [深入](#深入)
1. [鉴定者的顺序](#鉴定者的顺序)
2. [鉴定者名字](#鉴定者名字)
3. [一进多出](#一进多出)
3. [提高复用性](#提高复用性)
1. [串行鉴定者](#串行鉴定者)
2. [串行解析器](#串行解析器)
3. [前置鉴定者、后置鉴定者](#前置鉴定者-后置鉴定者)
4. [前置解析器、后置解析器](#前置解析器-后置解析器)
<!-- /code_chunk_output -->
-------------
> 如果想要更深入的了解,你可查看 [设计与机制][] 和 [API接口文档][]
# 入门
以用 `recogn-parse` 实现预览文件为例:
1. 创建 RecognParse 实例
首先需要创建一个 RecognParse 的实例:
```ts
const recognParse = new RecognParse();
```
2. 添加识别器
为了识别出不同的文件格式,我们需要添加一个用于识别格式的鉴定者,它可以是一个函数,也称为 鉴定函数:
```ts
// 扩展名鉴定者
function extnameJudger(target: any, targetOptions: Options, preResult: FormatJudgeResult){
// 获取文件路径
const path = getPath(target);
// 获取扩展名
const type = getExtname(path);
return {
type: type,
content:target
}
}
recognParse.recognizer.add(extnameJudger);
```
3. 添加解析器
我们需要为支持的文件类型添加解析器,它可以是一个函数,也称为 解析函数:
```ts
// pdf 解析器
async function pdfParser(target: any, targetOptions: Options, preResult: any, recognResult: JudgeInfo) {
// 请求文件内容
const data = await request(target);
// 查看
viewPdf(data);
return data
}
recognParse.resolver.add("pdf",pdfParser);
// json 解析器
async function jsonParser(target: any, targetOptions: Options, preResult: any, recognResult: JudgeInfo){
// 请求文件内容
const data = await request(target);
const jsonObj = JSON.parse(data);
// 查看
viewTxt(jsonObj);
// 可以返回外部需要使用的数据,也可以不返回
return jsonObj
}
recognParse.resolver.add("json",jsonParser);
```
4. 使用
当添加好 鉴定者 和 解析器后,就可以如下使用了:
```ts
recognParse.resolve("https://guo.binyou.com/recogn-parse.pdf").then((recognParseInfo:RecognParseInfo)=>{
// recognParseInfo 对象的 键 是识别出的类型,值 是 对应的识别结果(保存在 `recogn` 属性上) 和 解析结果(保存在 `parse` 属性上)
const data = recognParseInfo.pdf?.parse;
console.log("pdf的文件内容是:",data);
})
```
# 深入
截止目前,我们只添加了一个鉴定者,该鉴定者的作用就是提取出文件的扩展名,然后把扩展名解析使用的类型。大部分文件可以这样做,但也有一些例外,比如下面这些格式:
+ `GeoJSON`:这是一种使用 json 格式来描述的 地理失量图形格式。
+ `gltf`:这是一种使用 json 格式来描述的 3D 场景格式。
那我们就需要为这两种格式添加鉴定者了,以 `GeoJSON` 为例
```ts
// GeoJSON 鉴定者
async function geoJSONJudger(target: any, targetOptions: Options, preResult: FormatJudgeResult){
// 获取文件路径
const path = getPath(target);
// 获取扩展名
const type = getExtname(path);
if (type !== "json") return null;
const const data = await request(target);
const jsonObj = JSON.parse(data);
if (!isGeoJSON(jsonObj)) return null;
return {
type: "GeoJSON",
content:jsonObj
}
}
recognParse.recognizer.add(geoJSONJudger);
```
因为这个鉴定者需要获取到具体的数据才能识别文件内容的类型,所以这个鉴定者是异步函数,返回的是 `Promise`。
还需要为 `GeoJSON` 类型添加对应的解析器,如下:
```ts
// GeoJSON 解析器
function geoJsonParser(target: any, targetOptions: Options, preResult: any, recognResult: JudgeInfo) {
// 因为鉴定者已经获获取并解析了 json 文件的崆,所以这里不必重新获取,直接拿来用即可。
const data = recognResult.content;
// 查看
viewGeoJson(data);
return data
}
recognParse.resolver.add("GeoJSON",geoJsonParser);
```
## 鉴定者的顺序
因为鉴定者是从前往后依次执行,直到第一个成功鉴定的鉴定者为止,如下图所示:
![对鉴定者列表执行识别][]
又因为 `extnameJudger` 鉴定者一定会成功识别,所以,如果将这两个鉴定者是添加在 `extnameJudger` 之后,鉴定就不会走到 `geoJSONJudger`,所以,应该把更具体的子类的鉴定者放在 范围更广的鉴定者前面,在这里是把 `geoJSONJudger` 放在 `extnameJudger` 的前面,调整后的代码如下:
```ts
recognParse.recognizer.add(geoJSONJudger);
recognParse.recognizer.add(extnameJudger);
```
## 鉴定者名字
在添加鉴定者时,还可以给鉴定者指定一个可选的名字,示例如下:
```ts
recognParse.recognizer.add(gltfJudger,"GeoJSON");
recognParse.recognizer.add(extnameJudger,"extname");
```
在使用 `resolve` 时可以明确指定鉴定者的名字,这样可以直接命中对应的监定者,提高执行效率,如下:
```ts
recognParse.resolve("https://guo.binyou.com/GeoJSON.json",null,{recogn:"GeoJson"})
```
如果鉴定品的名字 和 解析器的 类型 相同,则可以使用使用 `RecognParse` 来添加,如下:
```ts
recognParse.add("GeoJSON",{
judger:geoJSONJudger,
parser:geoJsonParser,
});
```
这时 `resolve` 仍然可以通过 `recogn` 选项来指定鉴定鉴定的名字,也可以通过 `type` 字段同时指定 鉴定者的名字 和 解析器的类型,如下:
```ts
recognParse.resolve("https://guo.binyou.com/gltf.json",null,{type:"gltf"})
```
如果想批量添加,则可以使用如下方法:
```ts
recognParse.addRecognParsers({
GeoJSON:{
judger:geoJSONJudger,
parser:geoJsonParser,
},
gltf:{
judger:gltfJudger,
parser:gltfParser,
},
...
})
```
## 一进多出
因为 `GeoJSON` 文件也是 json 格式的文件,所以,我们也可以展示 GeoJSON 图形的同时,展示 json 文件格式的内容。
简单的思路是分别调用 `resolve` 再次,如下:
```ts
// 以 GeoJSON 展示
recognParse.resolve("https://guo.binyou.com/GeoJSON.json");
// 强制以 json 格式展示
recognParse.resolve("https://guo.binyou.com/GeoJSON.json",null,{recogn:"extname"});
// 或者
// recognParse.resolve("https://guo.binyou.com/GeoJSON.json",null,{type:"json",noRecogn:true});
```
还有一种性能更高的方案:鉴定者 可以返回多个鉴定信息,如下:
```ts
// GeoJSON 鉴定者
async function geoJSONJudger(target: any, targetOptions: Options, preResult: FormatJudgeResult){
// 获取文件路径
const path = getPath(target);
// 获取扩展名
const type = getExtname(path);
if (type !== "json") return null;
const const data = await request(target);
const jsonObj = JSON.parse(data);
if (!isGeoJSON(jsonObj)) return null;
return [
{
type: "GeoJSON",
content:jsonObj
},{
type: "json",
content:jsonObj
}
];
}
recognParse.recognizer.add(geoJSONJudger);
```
这样,当命中该鉴定者时,该鉴定者会返回两个类型的鉴定信息,它会分成调用这两个类型对应的解析器。
# 提高复用性
recogn-parse 也做了一些能够增强复用性、利于提高性能等相关的设计,如下:
- 串行的鉴定者 和 解析器
- 前置、后置鉴定者
- 前置、后置解析器
## 串行鉴定者
鉴定者 `geoJSONJudger` 中前面的逻辑是获取扩展名,其实这就是 `extnameJudger` 所做的工具,这部分完全可以复用。
鉴定者也可以是个函数数组,称为 串行鉴定者,如下:
```ts
// GeoJSON 鉴定者
async function geoJSONJudger(target: any, targetOptions: Options, preResult: FormatJudgeResult){
// 获取前面鉴定函数识别的结果
const type = preResult[0]?.type;
if (type !== "json") return null;
const const data = await request(target);
const jsonObj = JSON.parse(data);
if (!isGltf(jsonObj)) return null;
return {
type: "GeoJSON",
content:jsonObj
}
}
// 鉴定者 可以是一个 鉴定函数 数组
recognParse.recognizer.add([extnameJudger,geoJSONJudger],"GeoJSON");
```
* 串行鉴定者就是一组鉴定函数,所有的鉴定函数都会依次执行,不论中间有没有失败,都会依次执行完所有的鉴定函数,并会以最后一个鉴定函数返回的结果为最终的鉴定结果。
* 串行鉴定者序列中的任何项识别失败都不会中止 串行鉴定者序列 的执行。即:即使 串行鉴定者序列 `SerialJudger` 中的某一个 `Judge` 返回 null 或者 抛出错误,则仍会执行此 串行鉴定者序列 `SerialJudger` 中的下一个 `Judge`
它的执行逻辑图如下:
![执行串行鉴定者][]
## 串行解析器
与 串行鉴定者 的作用类似,都是为了解耦合 和 增强复用性。
串行解析器就是一组解析函数,这些解析函数会依次执行,并会以最后一个解析函数返回的结果为最终的解析结果。需要注意的是:这组解析函数中如果有解析函数抛出错误,则会终止解决流程。
它的执行逻辑如下图:
![执行串行解析器][]
## 前置鉴定者、后置鉴定者
如果 `获取扩展名` 是很多鉴定者首先要处理的逻辑,那么也可以把这部分逻辑做成前置鉴定者,这样可以让所有的鉴定者都能共用,方法如下:
```ts
recognParse.recognizer.preJudgers.push(extnameJudger)
```
`recognParse.recognizer.preJudgers` 是一个数组,它里面放置的是前置的鉴定者,同样也有后置鉴定者 `recognParse.recognizer.postJudgers`,它们的执行逻辑如下图:
![识别][]
![对鉴定者列表执行识别][]
![执行鉴定者][]
## 前置解析器、后置解析器
与 前置鉴定者、后置鉴定者的作用 和 使用方式类似,都是为了解耦合 和 增强复用性。
```ts
recognParse.recognizer.preParses.push(parser)
recognParse.recognizer.postParses.push(parser)
```
它的执行逻辑图如下:
![解决][]