Hacktoberfest 2026:维护者为十月标记出来的 issue,仍然开放、适合新手。 浏览 Hacktoberfest issue

Pure map schemas are broken

未关闭
#341 1 条评论 0 个 reaction 已指派 0 人 在 GitHub 查看

还没有人认领这个 Issue。

评估

难度
4/5
预计耗时
3-5 天
新手友好度
28/100
Issue 类型
缺陷
描述清晰度
基本清楚
活跃度
停滞
技术栈
clojure, openapi
领域
api, backend

调研方向

从 compojure.api.coercion.core/named-coercion 和 compojure.api.coercion.schema/json-coercion-matcher 开始,然后跟踪 issue 中展示的 Muuntaja JSON 解码器配置。使用纯 map schema {s/Str s/Str} 重现一个请求;完成的标准是字符串键在反序列化和 coercion 过程中仍保持为字符串,同时不破坏 schema-keyed map。

由索引模型根据 Issue 内容生成。

描述

Not sure if thats by design, since Swagger doesn't seem to represent well pure maps, i.e. {s/Str s/Str}, but handling of those is broken atm.

What happens is that the default implementation forces Cheshire to keywordize all incoming maps while deserializing, so instead of {s/Str s/Str}, the request winds up as {s/Keyword s/Str} :(

The good (for my use case) news is that since Compojure API is almost infinitely extensible ;) I was able to:

  1. Override the JSON deserialization
  2. Plug a custom coercer

Something like this (only the important bits):

(ns blah.core
  (:require
           ;; just to show what cs and cc below resolve to
            [compojure.api.coercion.core :as cc]
            [compojure.api.coercion.schema :as cs])

;; coercion wiring

(defn keywordize-some
  "Keywordizes only the top-level map keys that are in the ks collection"
  [ks m]
  (reduce (fn [nm k]
            (if (keyword? k)
              (if-let [existing (get nm k)]
                nm
                (let [k-str (name k)]
                  (if-let [ex2 (get nm k-str)]
                    (-> nm
                        (assoc k ex2)
                        (dissoc k-str))
                    nm)))
              nm))
          m
          ks))

(defn json-coercer [schema x]
  "Coerces a map value with a map schema - otherwise falls back to the original coercer"
  (if (map? x)
    (keywordize-some (keys schema) x)
    (if-let [fallback (cs/json-coercion-matcher schema)]
      (fallback x)
      x)))

(defn jcm [schema]
  (if (map? schema)
    (partial json-coercer schema)
    (cs/json-coercion-matcher schema)))

(defmethod cc/named-coercion :json [_] (cs/create-coercion {:body     {:default (constantly nil)
                                                                       :formats {"application/json"    jcm
                                                                                 "application/msgpack" jcm
                                                                                 "application/x-yaml"  jcm}}
                                                            :string   {:default cs/string-coercion-matcher}
                                                            :response {:default (constantly nil)}}))

(api
    ....
     ;; wire a non-keywordizing JSON deserializer
     ;; the actual keywordization will be done in a custom coercer
     :formats    (-> (:formats compojure.api.api/api-defaults)
                     (assoc-in [:formats muuntaja.format.json/json-type] {:decoder [muuntaja.format.json/make-json-decoder {}]
                                                                          :encoder [muuntaja.format.json/make-json-encoder]}))

     ;; our custom coercer registered via defmethod above
     :coercion   :json
    ...
     }
主要语言
Clojure
星标
1.1k
派生
144
PR 合并指标
30 天内没有已合并 PR

环境准备

  • 没有 Dockerfile 或 Docker Compose 文件
  • 没有 Pull Request 模板
  • 阅读贡献指南

从这里开始

  1. 先读完整个 Issue,再读项目的贡献指南。
  2. 在 Issue 下留言说明你要接手 —— 这能避免两个人做同样的事。
  3. Fork 仓库,在一个分支上完成修改。
  4. 提交 Pull Request,并在描述里引用这个 Issue 编号。

metosin/compojure-api 的其他 Issue

查看 metosin/compojure-api 的全部 Issue

相似的 Issue

更多 Clojure Issue

把新 issue 发到你的邮箱

精选适合新手参与的 GitHub issue 摘要。