十年匠心定制 · 商业建站与技术教学双线并行 咨询热线:400-886-1026 service@lmnt.cn
ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

KubeEdge 中 vendored 的 gojsonpointer:JSON Pointer(RFC 6901)在 Go 中的 Get、Set、Delete 实现解析

KubeEdge 中 vendored 的 gojsonpointer:JSON Pointer(RFC 6901)在 Go 中的 Get、Set、Delete 实现解析 KubeEdge 中 vendored 的 gojsonpointerJSON PointerRFC 6901在 Go 中的 Get、Set、Delete 实现解析【免费下载链接】kubeedgeKubernetes Native Edge Computing Framework (project under CNCF)项目地址: https://gitcode.com/GitHub_Trending/ku/kubeedge本文以 KubeEdge 仓库 vendor 目录下的vendor/github.com/xeipuuv/gojsonpointer依赖文档为主体完整继承其 README 中的用法示例并结合仓库内 pointer.go 的源码逐行解析 JSON Pointer 的解析规则、三种操作Get/Set/Delete的实现路径与 RFC 6901 语义边界帮助你在 KubeEdge 及相关 Go 项目中正确定位、修改和删除 JSON 文档中的深层节点。这个依赖在 KubeEdge 仓库中的位置gojsonpointer是一个单文件的 Go 库实现了 RFC 6901 定义的 JSON PointerJSON 指针规范即“用字符串路径精确指向 JSON 文档中某个节点”的标准方式。在 KubeEdge 仓库中源码与文档位于 README.md 和 pointer.go约 211 行附带 Apache-2.0 许可证在 go.mod 中它被标记为// indirect间接依赖github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb与gojsonreference、gojsonschema同属一个依赖簇从源码结构看KubeEdge 的 keadm 工具通过 helm 渲染器 引用了 Helm 的chartutil包其中调用chartutil.ToRenderValues组装渲染值而 Helm 的 JSON Schema 校验能力依赖 gojsonschema后者又依赖 gojsonpointer——因此这个库最终被 vendor 进仓库并参与构建尽管 KubeEdge 的业务代码并未直接 import 它。理解它的关键价值在于JSON Pointer 是 Helm、JSON Patch、JSON Schema 等生态组件通用的“寻址语言”吃透这个最小实现解析 遍历 三种操作就掌握了整条依赖链的底层行为。原始文档的完整用法示例下面是 README.md 中给出的完整示例按文档原样继承并对Delete一行的返回值接收做了使其可编译的修正——NewJsonPointer返回(JsonPointer, error)两个值package main import ( encoding/json fmt gojsonpointer github.com/xeipuuv/gojsonpointer ) func main() { jsonText : { name: Bobby B, occupation: { title : King, years : 15, heir : Joffrey B } } var jsonDocument map[string]interface{} json.Unmarshal([]byte(jsonText), jsonDocument) // 创建 JSON 指针 pointerString : /occupation/title pointer, _ : gojsonpointer.NewJsonPointer(pointerString) // SET给 title 设置新值 pointer.Set(jsonDocument, Supreme Leader of Westeros) // GET取回新的 title title, _, _ : pointer.Get(jsonDocument) fmt.Println(title) // 输出 Supreme Leader of Westeros // DELETE删除 heir deletePointer, _ : gojsonpointer.NewJsonPointer(/occupation/heir) deletePointer.Delete(jsonDocument) b, _ : json.Marshal(jsonDocument) fmt.Println(string(b)) // 输出 {name:Bobby B,occupation:{title:Supreme Leader of Westeros,years:15}} }示例覆盖了该库的全部公开 APIAPI签名摘自 pointer.go语义NewJsonPointerfunc NewJsonPointer(jsonPointerString string) (p JsonPointer, err error)解析指针字符串返回可复用的JsonPointer值Getfunc (p *JsonPointer) Get(document interface{}) (interface{}, reflect.Kind, error)按指针取值同时返回值的reflect.KindSetfunc (p *JsonPointer) Set(document interface{}, value interface{}) (interface{}, error)就地修改文档中指针指向的值返回文档本身Deletefunc (p *JsonPointer) Delete(document interface{}) (interface{}, error)删除指针指向的键/数组元素返回文档本身Stringfunc (p *JsonPointer) String() string反序列化回指针字符串形式文档操作的载体统一是interface{}实践中即map[string]interface{}与[]interface{}递归组合也就是encoding/json反序列化后的默认类型这也是示例先json.Unmarshal的原因。指针字符串的解析规则NewJsonPointerpointer.go#L60-L73实现了 RFC 6901 对指针字符串的三条硬性约束空字符串指向文档根len(jsonPointerString) 0时直接返回referenceTokens保持为nil必须以/开头否则返回JSON pointer must be empty or start with a /错误常量const_invalid_start按/切分为 reference token 序列strings.Split(jsonPointerString[1:], /)去掉开头的/后逐段切分例如/occupation/title→[occupation, title]。String()方法是其逆操作将 token 用/拼回并补上起始/空指针返回空串。实现解析Get、Set、Delete 共用一条遍历路径从源码结构看三种操作并非三份独立逻辑Get、Set、Delete各自构造一个带mode字段GET/SET/DEL的implStruct然后调用同一个私有方法implementationpointer.go#L100-L182完成 token 逐个下钻仅在“是否最后一个 token”时按 mode 分支执行写操作。空指针的特殊分支len(p.referenceTokens) 0时直接返回整个文档pointer.go#L106-L112即空指针 Get 到根对象与 RFC 6901 一致。对 JSON 对象map的处理遍历到map[string]interface{}节点时pointer.go#L127-L143先对 token 做decodeReferenceToken解码转义还原键存在继续下钻若这是最后一个 tokenSET模式写值、DEL模式delete(v, decodedToken)键不存在且是最后一个 tokenSET模式允许创建新键v[decodedToken] i.setInValue而GET/DEL模式返回Object has no key %s错误。这一差异很重要JSON Pointer 的 Set 具备“创建路径末端”的能力而 Get 对缺失路径是严格报错的不会返回 nil 蒙混过关。对 JSON 数组slice的处理遍历到[]interface{}节点时pointer.go#L145-L168token 必须能被strconv.Atoi解析为整数否则报Invalid array index %s下标越界负数或 len(v)报Out of bound array[0,%d] index %d最后一个 token 时SET直接写v[tokenIndex]DEL的实现值得细看v[tokenIndex] v[len(v)-1] // 用最后一个元素覆盖被删位置 v[len(v)-1] nil // 清空末位帮助 GC v v[:len(v)-1] // 截断出一个新的 slice 头 previousNodes[ti-1].(map[string]interface{})[previousTokens[ti-1]] v // 把新 slice 写回父 map这里暴露了一个 Go 细节v v[:len(v)-1]只是修改了局部 slice 头父容器map里持有的仍是旧底层数组的引用所以必须把新 slice 显式写回上一层。从源码结构看这段代码同时隐含一个假设——父节点是map[string]interface{}对上一级做了类型断言若指针形如/a/b/0且b本身是数组的嵌套结构该断言可能失败属于该 vendored 版本的实现边界。引用 token 的转义与反转义RFC 6901 规定~是转义前缀~1表示/~0表示~。实现pointer.go#L196-L211严格遵循了顺序敏感的替换规则解码decodeReferenceToken先~1→/再~0→~若顺序颠倒~01会被错误还原编码encodeReferenceToken先~→~0再/→~1。因此键名本身含/或~的文档例如路径型字段path/x指针应写作~1形式该库才能正确寻址。原文明确标注的语义限制Note 部分README 末尾的 Note 是本依赖文档中必须继承的重要事实声明RFC 6901 的“4. Evaluation”部分从“If the currently referenced value is a JSON array, the reference token MUST contain either...”开始的内容未实现。对照 RFC 6901 第 4 节这句话指向的是数组求值规则中-记号的部分——-本应指向数组“最后一个元素之后的不存在的元素”用于在 Set 时向数组追加。结合 pointer.go 的数组分支可以看到token 只接受strconv.Atoi可解析的整数-会触发Invalid array index错误。因此使用这个 vendored 版本时不能用/arr/-向数组追加元素需要自行 append 后写回或改用其他库数组删除采用上文“末位覆盖 截断”的实现而非标准语义描述的“移除该元素”对调用方可见的结果虽等价元素顺序保持、长度减一但属于实现细节升级 vendored 版本前应重新核对。这些限制在 KubeEdge 仓库中无需修复——它只是 Helm/Schema 生态的间接依赖但若你在自己的 Go 项目中直接依赖 gojsonpointer这两点是选型前必须知晓的行为边界。在 KubeEdge 仓库中如何继续深入依赖声明go.mod 中gojsonpointer/gojsonreference/gojsonschema三行 indirect 依赖以及 go.sum 中对应的校验和依赖的消费方keadm 的 helm 渲染器chartutil.ToRenderValues等调用它是 keadm 安装/升级流程中把组件 chart 渲染为 manifest 的入口vendor 侧完整实现与许可证README.md、pointer.go、LICENSE-APACHE-2.0.txt。要点速查指针字符串必须为空或/开头空指针指向文档根对象寻址支持 Set 时创建末端新键Get/Del 对缺失键报错数组寻址仅接受非负整数下标-追加语义未实现原文档 Note 明确声明含~、/的键名必须按~0/~1转义解码/编码均有严格先后顺序在 KubeEdge 中它经由 keadm → Helm chartutil → gojsonschema 链路间接生效属于构建期 vendored 依赖业务代码不直接使用。【免费下载链接】kubeedgeKubernetes Native Edge Computing Framework (project under CNCF)项目地址: https://gitcode.com/GitHub_Trending/ku/kubeedge创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表