clojureclojure-java-interopedn

How can I avoid printing object hashcode while reading EDN file in to java objects using clojure


I have defined EDN reader for custom tags which creates new object on occurrence of custom tags. I am making use of edn/read-string to read EDN data.

(defn getXyz [str]
    (.getXyz (XyzBuilder.) str)
)

(defn custom-readers []
    {'xyz/builder getXyz}
)

(defn getSomeObj
    [filename myEdnFile]
    (edn/read-string
        {:readers custom-readers}
        (slurp myEdnFile))
)

My EDN file

{
   "version" "1.0"
   "xyz" #xyz/builder "testString"
}

When I print output of getSomeObj from java, it prints following output

{"version" "1.0", "Xyz" #object[com.java.sample.Xyz 0x6731787b "Xyz [type=type_1]"]}

I want to avoid getting object hashcode (i.e. #object[com.java.sample.Xyz 0x6731787b) in the return string.. Is there a way to achieve it?


Solution

  • Yes, you need to define a custom implementation of print-method for it:

    (defmethod print-method Xyz [obj writer]
      (.write writer (.getXyz obj)))
    

    You should replace (.getXyz obj) with whatever logic you want for what gets printed.

    Now, it is best if you provide one logic for printing in a human readable way, and one for printing in a way that can be read back. So that print will print in a human readable way, and pr will print in a way you can read back with your custom reader.

    (defmethod print-method Xyz [obj writer]
      (if *print-readably*
        (.write writer (str "#xyz/builder " "\"" (.getXyz obj) "\""))
        (.write writer (.getXyz obj))))
    

    When *print-readably* is true, you want to print in a machine readable way, so a way that edn/read-string can read it back again.

    Here's a full example, I used StringBuilder instead of Xyz, sinc it was convenient and by default it prints as the object memory location (what you said looked like a hash) just like what you were asking.

    (defn getXyz [str]
      (StringBuilder. str))
    
    (defn custom-readers []
      {'xyz/builder getXyz})
    
    (defn getSomeObj
      []
      (edn/read-string
       {:readers (custom-readers)}
       "{\"version\" \"1.0\"
         \"xyz\" #xyz/builder \"testString\"}"))
    
    (defmethod print-method StringBuilder [obj writer]
      (if *print-readably*
        (.write writer (str "#xyz/builder " "\"" (.toString obj) "\""))
        (.write writer (str "The xyz is: " (.toString obj)))))
    
    (getSomeObj)
    ;; => {"version" "1.0", "xyz" #xyz/builder "testString"}
    
    (pr-str (getSomeObj))
    ;; => "{\"version\" \"1.0\", \"xyz\" #xyz/builder \"testString\"}"
    
    (print-str (getSomeObj))
    ;; => "{version 1.0, xyz The xyz is: testString}"