jsonista icon indicating copy to clipboard operation
jsonista copied to clipboard

`nil` as a key in map throws an Exception

Open DeLaGuardo opened this issue 2 years ago • 2 comments

I'm getting an exception trying to write to string a map with nil as a key.

(jsonista.core/write-value-as-string {nil 1}) ;; => throws com.fasterxml.jackson.databind.JsonMappingException

1. Unhandled com.fasterxml.jackson.databind.JsonMappingException
   Null key for a Map not allowed in JSON (use a converting NullKeySerializer?)
   (through reference chain: clojure.lang.PersistentArrayMap["null"])

 JsonMappingException.java:  281  com.fasterxml.jackson.databind.JsonMappingException/from
   SerializerProvider.java: 1336  com.fasterxml.jackson.databind.SerializerProvider/mappingException
   SerializerProvider.java: 1230  com.fasterxml.jackson.databind.SerializerProvider/reportMappingProblem
    FailingSerializer.java:   32  com.fasterxml.jackson.databind.ser.impl.FailingSerializer/serialize
        MapSerializer.java:  791  com.fasterxml.jackson.databind.ser.std.MapSerializer/serializeFields
        MapSerializer.java:  764  com.fasterxml.jackson.databind.ser.std.MapSerializer/serializeWithoutTypeInfo
        MapSerializer.java:  720  com.fasterxml.jackson.databind.ser.std.MapSerializer/serialize
        MapSerializer.java:   35  com.fasterxml.jackson.databind.ser.std.MapSerializer/serialize
DefaultSerializerProvider.java:  480  com.fasterxml.jackson.databind.ser.DefaultSerializerProvider/_serialize
DefaultSerializerProvider.java:  319  com.fasterxml.jackson.databind.ser.DefaultSerializerProvider/serializeValue
         ObjectMapper.java: 4487  com.fasterxml.jackson.databind.ObjectMapper/_writeValueAndClose
         ObjectMapper.java: 3742  com.fasterxml.jackson.databind.ObjectMapper/writeValueAsString
                  core.clj:  238  jsonista.core/write-value-as-string
                  core.clj:  233  jsonista.core/write-value-as-string
                      REPL:   10  clojud.utils.kafka/eval82873
                      REPL:   10  clojud.utils.kafka/eval82873
             Compiler.java: 7177  clojure.lang.Compiler/eval
             Compiler.java: 7132  clojure.lang.Compiler/eval
                  core.clj: 3214  clojure.core/eval
                  core.clj: 3210  clojure.core/eval
    interruptible_eval.clj:   87  nrepl.middleware.interruptible-eval/evaluate/fn/fn
                  AFn.java:  152  clojure.lang.AFn/applyToHelper
                  AFn.java:  144  clojure.lang.AFn/applyTo
                  core.clj:  665  clojure.core/apply
                  core.clj: 1973  clojure.core/with-bindings*
                  core.clj: 1973  clojure.core/with-bindings*
               RestFn.java:  425  clojure.lang.RestFn/invoke
    interruptible_eval.clj:   87  nrepl.middleware.interruptible-eval/evaluate/fn
                  main.clj:  437  clojure.main/repl/read-eval-print/fn
                  main.clj:  437  clojure.main/repl/read-eval-print
                  main.clj:  458  clojure.main/repl/fn
                  main.clj:  458  clojure.main/repl
                  main.clj:  368  clojure.main/repl
               RestFn.java: 1523  clojure.lang.RestFn/invoke
    interruptible_eval.clj:   84  nrepl.middleware.interruptible-eval/evaluate
    interruptible_eval.clj:   56  nrepl.middleware.interruptible-eval/evaluate
    interruptible_eval.clj:  152  nrepl.middleware.interruptible-eval/interruptible-eval/fn/fn
                  AFn.java:   22  clojure.lang.AFn/run
               session.clj:  202  nrepl.middleware.session/session-exec/main-loop/fn
               session.clj:  201  nrepl.middleware.session/session-exec/main-loop
                  AFn.java:   22  clojure.lang.AFn/run
               Thread.java:  829  java.lang.Thread/run

I tried to set :encode-key-fn option but with the same result. Looks like my function is not executed at all.

DeLaGuardo avatar Aug 26 '21 14:08 DeLaGuardo

@DeLaGuardo How would you expect the following object to be encoded? null is not a valid keyword in JSON AFAIK.

FieryCod avatar Sep 14 '21 07:09 FieryCod

Right, it is not valid but Cheshire encodes it as an empty string by default. I think throwing an exception is a good default behaviour but would be great to have some option to specify what to do with nil keys as an option for write-value function. That is possible right now but it requires custom NullKeySerializer implementation so the overall setup become a bit bloated.

Example:

class NullKeySerializer extends StdSerializer<Object> {
    public NullKeySerializer() {
        this(null);
    }

    public NullKeySerializer(Class<Object> t) {
        super(t);
    }
    
    @Override
    public void serialize(Object nullKey, JsonGenerator jsonGenerator, SerializerProvider unused) 
      throws IOException, JsonProcessingException {
        jsonGenerator.writeFieldName("");
    }
}
(.setNullKeySerializer
 (.getSerializerProvider j/default-object-mapper)
 (NullKeySerializer.))

DeLaGuardo avatar Sep 14 '21 08:09 DeLaGuardo