Сериализация дезаписи Clojure ClassNotFoundException

Я пытался сериализовать одну из своих записей в удобочитаемый формат. Пока сериализация с использованием сериализатора Java работала нормально, я пытаюсь использовать print-dup. Проблема, с которой я столкнулся, заключается в том, что при записи записи все идет нормально, чтение записи приводит к clojure.lang.LispReader$ReaderException: java.lang.ClassNotFoundException: common.dummy.Doodh. Я путаю пространства имен или что-то в этом роде? Обратите внимание, что это не проблема с сериализацией Java. Код ниже в простейшем виде

(ns common.dummy)

   (defrecord Doodh [id name])

   (defn output [filename obj]
    (def trr(map->Doodh {:id "moooh" :name "Cows"}))
    (def my-string (binding [*print-dup* true] (pr-str trr)))
    (spit filename my-string)
   )

   (defn pull [filename]
     (def my-data (with-in-str (slurp filename) (read)))
     (println my-data)
   )

содержимое текстового файла:

#common.dummy.Doodh["moooh", "Cows"]

person Cyrax    schedule 01.08.2013    source источник
comment
Кстати, что такое Дуд?   -  person Leon Grapenthin    schedule 01.08.2013
comment
Невозможно воспроизвести ошибку. Сообщите `*clojure-version* и ваш тест, вызвавший исключение.   -  person A. Webb    schedule 01.08.2013
comment
Дуд = Молоко. Отсюда мохи и коровы.   -  person Cyrax    schedule 02.08.2013
comment
@A.Webb Уэбб Версия Clojure {: основная 1,: второстепенная 5,: инкрементная 1,: квалификатор ноль}. Не совсем уверен, что это уместно, но я использую ring, lacij, tikkba. Как я уже отмечал ранее, это прекрасно работает, когда я выполняю чистую сериализацию на основе Java. Обратите внимание на пространство имен. Ошибка, о которой я сообщил, не из REPL   -  person Cyrax    schedule 02.08.2013


Ответы (1)


  • Не используйте def внутри определений функций. Когда вы используете def, вы создаете переменную в своем пространстве имен и, возможно, манипулируете ею как побочным эффектом при каждом вызове функции. Используйте let-блоки.

  • Если вы хотите сохранить структуры данных Clojure в файле, используйте clojure.edn. Это безопасно (например, без вашего ведома никакие функции, определенные в файле, не будут вызываться), но позволяет включить пользовательские считыватели (подробнее см. ниже).

  • Тип, определенный с помощью defrecord, может быть напечатан в (Clojure-reader-)удобочитаемом виде с использованием pr-str (спасибо @A. Webb за это замечание). В вашем примере я не понимаю, почему бы вам вообще не придерживаться хэш-карты, но если вам действительно нужна дезапись, вы можете преобразовать ее в удобочитаемую строку, прежде чем записывать ее в файл.

    (defrecord Doodh [id name])
    
    (defn output [filename obj]
      (spit filename (pr-str obj))
    
    (defn pull [filename]
      (with-in-str (slurp filename)
                   (read)))
    
    • This way of doing it has several disadvantages.
      • Using read makes your code vulnerable to function calls in the slurped files (like #=(java.lang.System/exit 0)).
      • Исключение будет вызвано, когда файл с именем файла будет пуст.
      • Наконец, ваш сохраненный файл станет несовместимым с вашим кодом, когда вы переместите объявление об отмене записи в другое пространство имен.
      • Всех трех недостатков можно избежать, используя edn-reader.

Использование пользовательского считывателя с EDN

  1. Мы расширяем наш тип Doodh, реализуя метод toString интерфейса java.lang.Object:

    (defrecord Doodh [id name]
      Object
      (toString [this] (str "#Doodh" (into {} this))))
    
  2. #P6#
    (spit "Doodh.edn" (map->Doodh {:id "134" :name "Berta"}))
    
    <блочная цитата> #P7#
  3. Теперь, чтобы убедиться, что Doodh будет прочитан, мы вызываем clojure.edn/read-string с пользовательской функцией чтения:

    (defn pull [filename]
      (->> (slurp filename)
           (clojure.edn/read-string {:readers {'Doodh map->Doodh}})))
    
  4. Если вы прочитаете «Doodh.edn» с помощью нового извлечения, вы должны получить действительный Doodh. В РЕПЛ:

    (pull "Doodh.edn")
    => #user.Doodh{:id 134, :name "Berta"}
    
person Leon Grapenthin    schedule 01.08.2013
comment
К какой `*clojure-версии* относится ваш ответ? Я думаю, что ваш третий пункт может быть устаревшим. - person A. Webb; 01.08.2013
comment
1.5.1. Вы подразумеваете, что он автоматически выбирает читателя-fn для #user.Doodh? - person Leon Grapenthin; 01.08.2013
comment
О, вы имеете в виду читателя edn? Нет, но Clojure reader вызовет конструктор. Фраза не может быть напечатана в (Clojure-reader-)читаемом виде является путаницей. - person A. Webb; 01.08.2013
comment
Другими словами, (with-in-str (pr-str (common.dummy.Doodh. "foo" "bar")) (clojure.core/read)) — это программа для чтения Clojure, и она прекрасно работает. Но да, (with-in-str (pr-str (common.dummy.Doodh. "foo" "bar")) (clojure.edn/read)) будет жаловаться на отсутствие функции чтения для edn. - person A. Webb; 01.08.2013
comment
Большое спасибо за разъяснения. Я не знал, что читатель Clojure способен считывать распечатанные derecords обратно. Я обновлю свой ответ. - person Leon Grapenthin; 01.08.2013
comment
@lgrapenthin: спасибо за подсказку EDN. Это сработало для меня, используя записи. Мне нужно использовать записи, и код, который я написал здесь, был предназначен только для того, чтобы сузить проблему до изолированного и легко читаемого раздела. - person Cyrax; 02.08.2013
comment
@A.Webb: Забавно. Я попробовал именно то, что вы сказали (with-in-str (pr-str (common.dummy.Doodh. foo bar)) (clojure.core/read)) и получил ** clojure.lang.LispReader$ReaderException: java .lang.ClassNotFoundException: ошибка common.dummy.Doodh **. Если это имеет значение, я использую Eclipse с Лейном. - person Cyrax; 02.08.2013