Search code examples
jsonclojurecheshire

How to represent non-standard java objects when encoding to JSON in Clojure?


I have a standard clojure map of things. The keys are keywords, and the values are arbitrary values. They can be nil, numbers, strings, or any other kind of JVM object/class.

I need to know how to encode this map into JSON, so that "normal" values map to the usual JSON values (e.g. keywords -> strings, integers -> JSON numbers, etc.), while values of any other class map to the string representations of those values, like this:

{
  :a 1
  :b :myword
  :c "hey"
  :d <this is an "unprintable" java File object>
}

gets encoded thus:

{ "a": 1, "b": "myword", "c": "hey", "d": "#object[java.io.File 0x6944e53e foo]" }

I want to do this because my program is a CLI parsing library and I'm working with the caller of the library to build this map up, so I don't exactly know what types of data will be in it. However, I would like to print it to the screen all the same to aid the caller in debugging. I have tried to naively give this map to cheshire, but when I do, cheshire keeps choking with this error:

Exception in thread "main" com.fasterxml.jackson.core.JsonGenerationException: Cannot JSON encode object of class: class java.io.File: foo

Bonus: I am trying to keep my dependency counts down and I have already vetted cheshire as my JSON library, but full marks if you can find a way to do the above without it.


Solution

  • With cheshire you can add an encoder for java.lang.Object

    user> (require ['cheshire.core :as 'cheshire])
    nil
    
    user> (require ['cheshire.generate :as 'generate])
    nil
    
    user> (generate/add-encoder Object (fn [obj jsonGenerator] (.writeString jsonGenerator (str obj))))
    nil
    
    user> (def json (cheshire/generate-string {:a 1 :b nil :c "hello" :d (java.io.File. "/tmp")}))
    #'user/json
    
    user> (println json)
    {"a":1,"b":null,"c":"hello","d":"/tmp"}