UNPKG

recogn-parse

Version:

recogn-parse 提供了 识别--解析 的机制,使用者只需要添加识别器 和 解析器,然后就可以对事件进行识别和解析,适合用所有具备 识别--解析 流程的场景中,如:加载并显示各种模型、预览文件等

334 lines (246 loc) 11.1 kB
[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) ``` 它的执行逻辑图如下: ![解决][]