deck.gl JSON 模块转换参考深入解析 type、function、 与 # 前缀体系【免费下载链接】deck.glWebGL2 powered visualization framework项目地址: https://gitcode.com/GitHub_Trending/de/deck.gldeck.gl/json是 deck.gl 的 JSON 驱动层它允许开发者用纯 JSON 声明图层、视图与控制器从而让可视化配置可以被序列化、远程下发或嵌入低代码场景。本文以官方 conversion-reference.md 为骨架结合 modules/json/src 源码与 test/modules/json 测试完整讲解type、function、、#五大语法前缀的转换规则、配置注册方式与底层执行原理读完后你将能独立编写可被JSONConverter正确解析的 deck.gl JSON 配置。转换机制总览deck.gl/json框架在把原始的 JSON 数据结构交给 deck.gl 作为 props 之前会先对其做一次**水合hydration**转换扫描解析后的 JSON把结构中的特定对象替换为真正的 JavaScript 实例。转换后的产物是可直接用于new Deck({...})的运行时 props。转换的入口是JSONConverter.convert()见 json-converter.ts其核心流程为若传入的是 JSON 字符串先经 parse-json.ts 的JSON.parse转为对象对对象做递归转换convertJSONRecursively数组逐元素处理字符串单独处理普通对象逐键处理转换完成后调用配置中注册的postProcessConvertedJson钩子做最终修正通过浅比较json this.json缓存结果避免同一份 JSON 被重复转换。JSONConverter接受两种配置形式直接传入一个普通配置对象或传入JSONConfiguration实例若是普通对象内部会先包装成JSONConfigurationsetProps。语法前缀速查表以下五类前缀定义于 syntactic-sugar.ts是 JSON 与 JavaScript 世界的翻译指令前缀说明用法示例type将字符串解释为 JavaScript 类或 React 组件在JSONConfiguration中解析type: ScatterplotLayerfunction将字符串解释为 JavaScript 函数在JSONConfiguration中解析function: calculateRadius将字符串剩余部分解释为一个函数未加引号的字符串会被解析为标识符[lng, lat]#将字符串剩余部分解释为一个常量在 JSON 配置中解析#MapController#enum-name.enum-value将字符串剩余部分解释为一个枚举值在 JSON 配置中解析#GL.ONE注意type与function是对象级指令以键值对形式出现在 JSON 对象内部而与#是字符串级指令直接作为字符串值出现。JSONConfiguration默认的键名typeKey/functionKey可通过配置覆盖json-configuration.ts。Classes 与type的使用类转换是默认开启的能力。当把如下配置传给JSONConverter时——把各模块的图层类合并注册为 class catalogconst configuration { classes: Object.assign({}, require(deck.gl/layers), require(deck.gl/aggregation-layers)) };用该配置解析如下 JSON 对象{ layers: [ { type: ScatterplotLayer, data: ..., getColor: [0, 128, 255], getRadius: 1 } ] }转换后图层描述符会被替换为真正的类实例{ layers: [ new ScatterplotLayer({ data: ..., getColor: [0, 128, 255], getRadius: 1 }) ] }如果指定的图层名未注册转换器会通过配置中的log输出一条警告测试JSONConverter#badConvert验证了这一点见 json-converter.spec.ts。从源码看未注册类型会被instantiateClass返回null并调用log.warn(JSON converter: No registered class of type ...)instantiate-class.ts。只要JSONConverter发现type字段就会去查询类似上例的 class catalog。这些类可以是图层、视图或其他任意对象前提是它们已被注册。解析时的实际顺序是convertClassInstance取出type对应的类型名从 props 中剔除该键对剩余 props 做递归转换调用instantiateClass(type, props, configuration)若命中 JS 类则先经过preProcessClassProps钩子与convertFunctions把字符串编译为访问器函数再new Class(props)若命中 React 组件则走React.createElement分支instantiate-class.ts。React 组件与type实验性type同样支持 React 组件的水合。例如将组件注册进reactComponents并把 React 运行时注入配置import React from react; import TestComponent from /components/test; const configuration { React, reactComponents: { TestComponent } };用于解析如下 JSON 对象{ type: TestComponent, // props for the component color: [0, 128, 255], anotherProp: 1 }它会被替换为一个真正的 React 元素{ $$typeof: Symbol(react.element), key: null, props: { color: [0, 128, 255], anotherProp: 1 }, // ... }你可以直接把它拿来渲染。同样地若命名的 React 组件未注册会输出警告。需要留意的是React 组件水合依赖配置中的React运行时json-configuration.ts且组件 props 中的children会被单独取出通过React.createElement(Component, props, children)传入instantiate-class.ts。文档标注该能力为实验性experimental使用前请确认你的deck.gl/json版本行为。函数与function任意 JavaScript 函数都可以注册进配置的functions目录然后在 JSON 中通过function引用并传参。例如function calculateRadius({base, exponent}) { return Math.pow(base, exponent); } const configuration { ..., functions: {calculateRadius} };在 JSON 中这样使用{ layers: [ { type: ScatterplotLayer, data: ..., getColor: [0, 128, 255], getRadius: { function: calculateRadius, base: 2, exponent: 3 } } ] }转换后getRadius会被替换为函数计算结果{ layers: [ new ScatterplotLayer({ data: ..., getColor: [0, 128, 255], getRadius: 8 }) ] }若函数未注册会输出警告。JSONConverter遇到function字段时会去查询 function catalog。底层实现中executeFunction 会先剔除function键、递归转换剩余 props然后以整个 props 对象为参数调用注册函数matchedFunction(props)——也就是说function旁的其余键值对会成为该函数唯一的入参对象。测试JSONConverter#mergeFunctions验证了动态合并函数并执行的过程json-converter.spec.ts。常量与#前缀配置可以声明一张常量表把不需要实例化的值提供给 JSON 字符串解析器。#前缀会触发常量查找。例如向JSONConverter传入如下配置import {MapController} from deck.gl/core; const configuration { ... constants: { MapController } };用于解析这个 JSON 对象{ controller: #MapController, layers: [ { type: ScatterplotLayer, data: ..., ... } ] }转换后常量值会被替换为配置中声明的实际值{ controller: MapController, // MapController class from deck.gl/core layers: [ new ScatterplotLayer({ data: ..., ... }) ] }JSONConverter遇到#前缀的字符串时查找顺序是先查 constants catalog再查 enumerations catalog见 convertString。constants适合放置控制器类、工具函数引用等不需要参数化实例化的值。在 json-configuration-for-deck.ts 中MapController正是以常量形式注册测试断言deckProps.controller MapController验证了这条链路。枚举与#前缀deck.gl 可视化经常需要访问特定枚举值因此配置对象同样支持声明一张枚举表供 JSON 解析器使用#前缀同样会触发枚举查找。例如向JSONConverter传入配置import GL from luma.gl/webgl/constants; const configuration { ... enumerations: { GL } };解析如下 JSON 对象{ layers: [ { type: ScatterplotLayer, data: ..., coordinateSystem: meter-offsets, parameters: { blend: true, blendFunc: [#GL.ONE, #GL.ZERO, #GL.SRC_ALPHA, #GL.DST_ALPHA] } } ] }#enum-name.enum-value会被解析为enumerations配置中的实际数值{ layers: [ new ScatterplotLayer({ data: ..., coordinateSystem: meter-offsets, parameters: { blend: true, blendFunc: [1, 0, 770, 772] } }) ] }解析逻辑在 convertString 中去掉#前缀后先用string.split(.)拆出枚举组名与成员名再从configuration.config.enumerations[enumVarName][enumValName]取值。测试JSONConverter#convert断言PointCloudLayer的coordinateSystem被正确解析为COORDINATE_SYSTEM.METER_OFFSETSjson-converter.spec.ts可见 json-configuration-for-deck.ts 中COORDINATE_SYSTEM与GL都按枚举组名.成员名的约定注册。注意coordinateSystem的值 meter-offsets 是 JSON 层的字符串形式由转换器映射到枚举数值。内联函数表达式与前缀前缀用于从字符串直接生成访问器函数这是数据行row驱动的场景中最常用的语法。layers: [{ type: HexagonLayer, data: [ {lat:0,lng:0}, {lat:0,lng:0}, {lat:0,lng:0}, {lat:1.2,lng:1.2}, {lat:1.2,lng:1.2}, {lat:1.2,lng:1.2} ], getPosition: [lng, lat] }]此时会生成一个形如(datum) [datum[lng], datum[lat]]的函数从 JSON 数据行中读取字段。传递-则会返回一个恒等函数(datum) (datum)。例如当数据本身就是坐标数组如[[0, 1], [0, 5]]时-会直接原样返回这些值。在 parse-expression-string.ts 中-被预置在表达式缓存里作为 identity accessor。此外内置了一个小巧的 JavaScript 表达式解析器基于 jsep 的expression-eval支持基本的布尔运算、内联条件与算术运算。以下都是合法写法getPosition: [lng, lat, altitudeMeters], getFillColor: [color / 255, 200, 20], getLineColor: value 10 ? [255, 0, 0] : [0, 255, 200]每个都会被求值成等价表达式datum [datum.lng, datum.lat, altitudeMeters / 1000] datum [datum.color / 255, 200, 20] datum datum.value 10 ? [255, 0, 0] : [0, 255, 200]的底层编译与安全限制从源码看字符串最终被 parseExpressionString 编译编译结果按原字符串缓存cachedExpressionMap同一表达式只编译一次若 AST 根节点是Identifier纯字段名生成row get(row, propValue)的字段读取函数支持a.b.c形式的嵌套属性访问其他情况数组、算术、条件等编译为row evaluate(ast, row)即把数据行作为表达式求值的作用域安全限制编译前会遍历 AST一旦发现CallExpression函数调用语法就抛出Function calls not allowed in JSON expressions这是为了防止 JSON 表达式逃逸到全局作用域、造成任意代码执行。因此表达式中只能访问数据行里的纯数据不能调用Math、Date等全局对象针对字符串的转换发生在两个时机一是字符串值经 convertString 在递归转换阶段处理二是类实例化前由 convert-functions.ts 对 props 中的前缀字符串统一编译。配置目录与钩子的完整清单结合 json-configuration.ts一张完整的配置可以包含以下目录与钩子配置键类型作用classesRecordstring, Constructortype的类目录图层、视图等reactComponentsRecordstring, Functiontype的 React 组件目录enumerationsRecordstring, any#GROUP.VALUE的枚举目录constantsRecordstring, unknown#CONSTANT的常量目录functionsRecordstring, Functionfunction的函数目录React{createElement}React 运行时组件水合必需typeKey/functionKeystring自定义类型键/函数键名默认type/functionlogConsole未注册类型/函数时的警告输出convertFunctionFunction自定义字符串编译函数默认parseExpressionStringpreProcessClassPropsFunction类/组件实例化前的 props 改写钩子postProcessConvertedJsonFunction转换结果返回前的整体改写钩子JSONConfiguration.merge()支持将对象类目录深度合并Object.assign其余键直接覆盖测试JSONConverter#merge展示了通过mergeConfiguration({classes: {OrbitView}})动态追加视图类的用法json-converter.spec.ts。实践组装一份完整可运行的 JSON 配置综合以上语法参考仓库中 json-configuration-for-deck.ts 的注册方式一份生产可用的配置通常这样组织import {MapView, FirstPersonView, MapController, COORDINATE_SYSTEM} from deck.gl/core; import {GL} from luma.gl/webgl/constants; import * as deckglLayers from deck.gl/layers; import * as deckglAggregationLayers from deck.gl/aggregation-layers; import {JSONConverter} from deck.gl/json; const configuration { // 图层/视图类目录type 在此解析 classes: Object.assign({MapView, FirstPersonView}, deckglLayers, deckglAggregationLayers), // 函数目录function 在此解析 functions: { calculateRadius: ({base, exponent}) Math.pow(base, exponent) }, // 枚举目录#GROUP.VALUE 在此解析 enumerations: {COORDINATE_SYSTEM, GL}, // 常量目录#CONSTANT 在此解析 constants: {MapController} }; const jsonConverter new JSONConverter({configuration}); const deckProps jsonConverter.convert({ views: [{type: MapView, controller: true}], layers: [ { type: ScatterplotLayer, data: [{position: [0, 0], r: 2}, {position: [1, 1], r: 3}], getPosition: [position], getRadius: {function: calculateRadius, base: 2, exponent: 3}, getColor: r 2 ? [255, 0, 0] : [0, 128, 255] } ] }); // deckProps 即可直接作为 new Deck(deckProps) 的输入关键要点回顾对象级指令type、function与字符串级指令、#作用于不同位置混用时应保持层级清晰#查找顺序为常量目录优先、枚举目录兜底枚举需按组名.成员名写全表达式安全沙箱禁止函数调用语法只允许操作数据行字段未注册的类或函数不会抛错而是返回null并输出警告排查 JSON 报错时先看控制台警告相关单元测试见 json-converter.spec.ts、parse-expression-string.spec.ts 与 convert-functions.spec.ts可用于验证你手写 JSON 的行为是否符合预期。【免费下载链接】deck.glWebGL2 powered visualization framework项目地址: https://gitcode.com/GitHub_Trending/de/deck.gl创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考