gpt4 book ai didi

json - 如何将庞大的 Elisp 数据结构转换为 JSON?

转载 作者:行者123 更新时间:2023-12-01 05:14:39 25 4
gpt4 key购买 nike

org-element-parse-buffer即使对于一个小的 Org 文件,也会返回一棵巨大的树。我想将这棵树转换成 JSON。显然,json.el使用递归函数遍历 cons 单元,因为 Elisp 不支持 tail recursion , json-encode 的调用很快就用完了堆栈。如果我增加 max-lisp-eval-depthmax-specpdl-size,Emacs 会崩溃。

我该如何解决这个问题并将巨大的树结构转换为 JSON?一般来说,当我有一个庞大的数据结构和一个可能会用完堆栈的递归函数时,我该如何解决?

最佳答案

是的,json.el函数是递归的,但是在 Org-Element 上调用的递归函数导致堆栈溢出不是因为 org-element-parse-buffer返回一个巨大的 AST,但因为它返回一个 circular list .循环列表上的树递归函数就像笼子里的松鼠。

我想,在 AST 返回中使用自引用背后的想法是,如果你遍历它,在任何时候你都可以通过简单地运行 plist-get 返回到父级。关于关键字 :parent .我想象这种上下遍历 AST 的用法:

(let ((xs '#1=(:text "foo" :child (:text "bar" :parent #1#))))
(plist-get
(plist-get
xs
:child) ; (:text "bar" :parent (:text "foo" :child #0))
:parent)) ; (:text "foo" :child (:text "bar" :parent #0))

但 JSON 不支持循环列表,因此在尝试转换为任何数据序列化格式之前,您需要从 AST 中删除这些自引用。我还没有找到优雅地删除 AST 中的循环引用的方法,所以我求助于一个肮脏的 hack:

  1. 将 AST 转换为字符串
  2. 使用 regular expressions 删除引用
  3. 将字符串转换回 Elisp 数据结构

假设我有一个名为 test.org 的组织文件内容如下:

* Heading
** Subheading
Text

然后变量tree包含从此缓冲区解析的组织数据:(setq tree (with-current-buffer "test.org" (org-element-parse-buffer))) .然后为 JSON 导出准备此数据,我只需运行:

(car (read-from-string (replace-regexp-in-string ":parent #[0-9]+?" "" (prin1-to-string tree)))))

即使所有提及 :parent删除后,新的 AST 仍然有效,因此如果新的 AST 在变量 tree2 中, 那么下面3个表达式是等价的:

(org-element-interpret-data tree2)
(with-current-buffer "test.org" (buffer-substring-no-properties 1 (buffer-end 1)))
"* Heading\n** Subheading\nText\n"

请注意,出于某种原因 org-element-interpret-data删除前面的空格,所以当你有像 text 这样的行时,上面的内容在技术上是不正确的在您的组织文件中。

现在您需要做的就是将新的非循环 AST 编码为 JSON 并将其写入文件:

(f-write (json-encode tree2) 'utf-8 "test.json")

注意事项

  1. Elisp 的 cons cells是成对的 2 个插槽:carcdr .如果cdr每个单元格包含到另一个 cons 单元格的链接,我们得到一个链表。如果两个carcdr指向 2 个值,我们得到一个点对。因此(1 . (2 . (3 . nil)))相当于(1 2 3) .但是一个cdr (或 car 就此而言)可能指向任何其他 cons 单元格,包括已经在列表中较早的单元格,从而产生 circular linked list .

  2. 练习:创建一个复杂的树数据结构,其中包含对不同子树的多个自引用。然后尝试遍历这棵树并通过自引用跳转以获得想法。

  3. ->>来自 dash 的线程宏列表操作库表达式等同于:

    (->> tree prin1-to-string (replace-regexp-in-string ":parent #[0-9]+?" "") read-from-string car)
  4. (buffer-substring-no-properties 1 (buffer-end 1))就像(buffer-string) , 但没有烦人 text properties附上。

  5. f-write 是一个将文本写入来自 f 的文件的函数第三方文件操作库。

关于json - 如何将庞大的 Elisp 数据结构转换为 JSON?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/28970943/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com