2010-07-21 6 views
16

Ich habe eine komplexe Clojure-Datenstruktur, die ich serialisieren möchte - im Grunde den gesamten aktuellen Spielstatus für ein Online-Spiel, das ich entwickle, damit ich Spieldateien speichern kann.Clojure Datenstruktur Serialisierung

Meine Anforderungen sind:

  • Irgendeine Form von Menschen lesbaren Text-Format (ich wahrscheinlich Ausdrücke s-bevorzugen würden, JSON und XML in dieser Reihenfolge, sondern offen für andere)
  • Unterstützung alle üblichen Clojure Datenstrukturen, Schlüsselwörter und Primitiven
  • Fähigkeit benutzerdefinierte Serialisierung/Deserialisierung Funktionen für benutzerdefinierte Java-Klassen zur Verfügung zu stellen, defrecords usw. (dies ist wichtig, weil ich so etwas wie Java readResolve in mehreren Fällen tun müssen)
  • Gute Leistung ist ein Eis zu haben

Irgendwelche guten Empfehlungen?

+0

Ohne viel über Clojure zu wissen, gibt es einen Grund, warum dies nicht mit den von Clojure aufgerufenen Standard-Java-Serialisierungsmechanismen erreichbar ist? – Gian

+0

@Gian - Ja, das ist sicherlich möglich, aber ich versuche den "Clojure-Weg" zu lernen :-) – mikera

+0

IMO Der Clojure-Weg ist die Verwendung von Javas Einrichtungen, wo sie gute Lösungen für die Probleme bieten, die sie adressieren. :-) 'Serializable' kann eine gute Lösung für die kurzfristige Speicherung/Übertragung von Datenstrukturen sein. Ich nehme an, dass für diesen Anwendungsfall ein Format benötigt wird, das besser für die Langzeitspeicherung geeignet ist, und dies könnte von "print-dup" bereitgestellt werden. ('Serializable' könnte zu Problemen führen, wenn beispielsweise die Struktur der Klassen, die die Kern-Clojure-Datenstrukturen implementieren, sich ändert;' print-dup' wahrscheinlich nicht.) –

Antwort

11

Wenn Sie wollte die Dinge zu S-Ausdrücke serialisiert, könnten Sie print-dup verwenden:

(binding [*print-dup* true] (println [1 2 3])) 
; prints [1 2 3] 

(defrecord Foo [x]) 
; => user.Foo 
(binding [*print-dup* true] (println (Foo. :foo))) 
; prints #=(user.Foo/create {:x :foo}) 

Beachten Sie, dass eine Struktur Druck, die, sagen wir, zehn Verweise auf einem einzigen Vektor durch das Lesen es Ihnen gefolgt zurückhält gibt eine Datenstruktur mit zehn separaten (nicht identical?) Vektoren, die jedoch hinsichtlich der Struktur (=) gleichwertig sind.

Um dies zu verwenden, wenn keine Standardimplementierung bereitgestellt wird, implementieren Sie die Multimethode clojure.core/print-dup.

Auch eine Menge Dinge in Clojure 1.2 sind java.io.Serializable:

(every? (partial instance? java.io.Serializable) 
     [{1 2} #{"asdf"} :foo 'foo (fn [] :foo)]) 
; => true 

(defrecord Foo []) 
(instance? java.io.Serializable (Foo.)) 
; => true 

Beachten Sie, dass Laufzeit erstellte fn s Serialisierung vermeiden sollten - sie Instanzen von Einmal Klassen mit seltsamen Namen sind und Sie gewonnen Es ist nicht möglich, sie nach dem Neustart Ihrer JVM zu deserialisieren. Bei der AOT-Kompilierung erhalten fn s ihre eigenen festen Klassennamen.

Update: Wie in der Frage in einem Kommentar erwähnt, ist Serializable am besten geeignet, um die kurzfristige Lagerung/Übertragung von Daten, während print-dup als langfristige Speicherlösung robuster sein sollte (Arbeits in vielen Versionen die Anwendung, Clojure usw.). Der Grund dafür ist, dass print-dup in keiner Weise von der Struktur der Klassen abhängt, die serialisiert werden (so dass ein Vektor print-dup heute noch lesbar ist, wenn die Vektorimplementierung von Java zu Clojures deftype wechselt).

+0

Ich denke, dass "print-dup" die überlegene Lösung für diesen Anwendungsfall sein könnte, siehe meinen Kommentar zu der Frage ... Wenn die Speicherspiele groß werden, können die Ausdrucke immer komprimiert werden. –

3

für JSON können Sie Standard clojure-contrib.json verwenden. Obwohl, wie ich mich erinnere, sollten alle Clojure-Objekte serialisierbar sein ...

+0

Versucht dies, es mag meine Java-Klassen nicht: es wirft z java.lang.Exception: Ich weiß nicht, wie ich JSON der Klasse mikera.persistent.SparseMap schreiben soll. Gibt es eine Möglichkeit, benutzerdefinierte Serialisierungsfunktionen für Ihre eigenen Klassen zu geben? – mikera

+0

Sie könnten Ihre Klasse die 'clojure.contrib.json.Write_JSON' Schnittstelle implementieren (oder das Protokoll' clojure.contrib.json/Write-JSON', wenn die Klasse ein Clojure Datensatz/Typ ist). Nicht sicher, wie du sie lesen würdest; Ich denke, dass Sie für nicht standardmäßige Datenstrukturen etwas wie YAML verwenden möchten. Eine schnelle Google-Suche findet "clj-yaml" http://github.com/lancepantz/clj-yaml als eine mögliche Lösung, obwohl ich nichts wirklich über das Projekt weiß. –

+0

Ah, gerade bemerkt, dass 'CLJ-Yaml' README sagt, es unterstützt nur Deserialisierung für jetzt ... –

4

Wenn alles eine Clojure-Datenstruktur ist, dann ist sie bereits serialisiert (b/c von Code < -> Daten). Legen Sie die Datenstrukturen einfach auf die Festplatte ab. Um sie wiederherzustellen, lade sie zurück und (eval).

+0

Wie meldest du auf die Festplatte? – Zubair

+0

Binden Sie '* out *' an einen Dateistream und verwenden Sie '(pr)' (und verwenden Sie '(load-file)', um es zurückzulesen). Ein funktionierendes Beispiel finden Sie unter http://groups.google.com/group/clojure/browse_thread/thread/cb5246d07142a3dc?fwc=2&pli=1. – Greg

+1

Bewertet sie nicht gefährlich? Irgendjemand könnte etwas Code hineinschleichen. Sie sollten gelesen, aber nicht bewertet werden, glaube ich. – Pablo

6

edn-format wurde jetzt als Standard für die Datenübertragung mit Clojure Datenstrukturen freigegeben.

Es eignet sich sehr gut für die Serialisierung von Clojure-Datenstrukturen/-werten - und wird in mehreren Sprachen unterstützt, so dass es auch als Datenaustauschformat verwendet werden kann.

+0

das funktioniert gut für Core Clojure Datenstrukturen, aber sagen Sie hatten eine exotischere Datenstruktur darin wie eine PriorityMap (die aussieht wie eine PersistentMap, aber verhält sich anders), dann müssten Sie Reader Makros oder etwas richtig? – Hendekagon

+0

Sie haben dies jedoch im edn-Format: Sie können Werte "markieren", um anzuzeigen, dass sie von einem speziellen Typ sind und Ihre eigenen Handler schreiben, um die entsprechende Klasse zu konstruieren. Es ist hübscher celever! – mikera