2

我认为这是一个简单的问题,但作为 Clojure 的初学者,我想在 Clojure 中将简单的 JSON 转换为 EDN。

我的 JSON:

{
    "Data": [
        {
            "Metadata": {
                "Series": "1/2"
            },
            "Hybrid": {
                "Foo": 76308,
                "Bar": "76308",
                "Cat": "Foo123"
            }
        }
    ],
    "Footer": {
        "Count": 3,
        "Age": 0
    }
}

因此,如果我们假设数据是上面的 json,我尝试使用 Cheshire 将其转换为 EDN,如下所示:

(log/info "data" (cheshire.core/parse-string {(data) true}))

但是,每当我运行这段代码时,都会收到错误消息:

ERROR compojure.api.exception - clojure.lang.PersistentArrayMap cannot be cast to java.lang.String

我想我得到这个是因为我的 JSON 不是字符串,但不确定我是否需要先将其转换为字符串,然后再转换为 EDN——或者是否有办法可以直接从 JSON 转换为 EDN?

提前谢谢你的帮助

4

3 回答 3

3

您收到此行的此错误:

(cheshire.core/parse-string {(data) true})

这里发生了什么:

  1. 无论您的数据是什么,它似乎都可以调用 - 否则(data) 已经失败。例如 with("{}")会给你一个class java.lang.String cannot be cast to class clojure.lang.IFn错误。也不太可能,这data已经是反序列化的地图,因为地图是函数,但至少有一个。所以我们必须假设,这data实际上是一个函数并返回任何内容。
  2. 接下来,此调用的结果将作为键和值true( {? true}) 放入一个新映射中。
  3. 然后这张地图被传递给 cheshire's parse-string,但这显然不是字符串,而是一张地图,因此出现了错误。

鉴于所有这些,并假设(data)返回 a String,您最好的选择是:

(cheshire.core/parse-string (data))

如果你真的想要 EDN,那就是:

(pr-str (cheshire.core/parse-string (data)))
于 2020-07-16T10:40:47.717 回答
0

这将有助于澄清正在发生的事情。我们首先包含一个有用的库:

(ns tst.demo.core
  (:use tupelo.core tupelo.test)
  (:require
    [tupelo.core :as t]
    [tupelo.string :as str]
    ))

为了避免双引号对文字字符串和嵌入字符串的歧义,我们使用单引号编写源 JSON,然后使用辅助函数str/quotes->double将字符串中的每个单引号转换为双引号. 否则,我们可以从文件中读取源 JSON(而不是内联)。

(def json-str
  (str/quotes->double
    "{
      'Data': [
               {
                'Metadata': {
                             'Series': '1/2'
                            },
                'Hybrid': {
                           'Foo': 76308,
                           'Bar': '76308',
                           'Cat': 'Foo123'
                          }
               }
              ],
      'Footer': {
                 'Count': 3,
                 'Age': 0
                }
     } "))

我们首先将 json字符串转换为 EDN数据结构(不是字符串)。然后我们将 EDN 数据结构转换为EDN 字符串。输出(printlnprn)说明了差异:

(dotest
  (let [edn-data (t/json->edn json-str) ; JSON string => EDN data
        edn-str  (pr-str edn-data) ; EDN data => EDN string
        ]
    (newline)
    (println "edn-data =>")
    (spy-pretty edn-data)     ; uses 'prn'

    (newline)
    (println "edn-str (println) =>")
    (println edn-str)

    (newline)
    (println "edn-str (prn) =>")
    (prn edn-str)))

结果:

------------------------------------------
   Clojure 1.10.2-alpha1    Java 14.0.1
------------------------------------------

Testing tst.demo.core

edn-data =>
{:Data
 [{:Metadata {:Series "1/2"},
   :Hybrid {:Foo 76308, :Bar "76308", :Cat "Foo123"}}],
 :Footer {:Count 3, :Age 0}}

edn-str (println) =>
{:Data [{:Metadata {:Series "1/2"}, :Hybrid {:Foo 76308, :Bar "76308", :Cat "Foo123"}}], :Footer {:Count 3, :Age 0}}

edn-str (prn) =>
"{:Data [{:Metadata {:Series \"1/2\"}, :Hybrid {:Foo 76308, :Bar \"76308\", :Cat \"Foo123\"}}], :Footer {:Count 3, :Age 0}}"

仔细想想什么是数据结构,什么是字符串。如果我们写入[1 :b "hi"]Clojure 源文件,Clojure Reader 会创建一个数据结构,它是一个包含 int、关键字和字符串的 3 元素向量。如果我们使用 将其转换为字符串str,则输出只是一个 11 个字符的字符串,而不是数据结构。

但是,如果我们想将该字符串(双引号内)粘贴到源文件中,我们不仅需要外部双引号来标记字符串的开头和结尾,还需要字符串内的每个双引号(例如作为"hi") 的一部分需要转义,因此 Clojure 阅读器可以判断它们属于字符串内部,并且不标记字符串的开头或结尾。

适应所有不同的模式需要一段时间!


Clojure 阅读器与编译器

Clojure 源代码文件分两遍处理。

  1. Clojure Reader 获取每个源文件的文本(多行的巨大字符串)并将其转换为数据结构。

  2. Clojure 编译器从 (1) 中获取数据结构并输出 Java 字节码。

于 2020-07-16T06:46:43.950 回答
0

您收到此错误是因为您将变量数据作为 clojure Map 而不是作为字符串(或 json 对象)传递。

这实际上也是错误所说的。

cheshire.core/parse-string 需要一个 json 对象,该对象将是一个字符串。

'{ "name": "Cheshire", "needs": "a string"}'
于 2020-07-16T13:21:51.957 回答