| 1 |
(in-package :bknr.impex) |
|---|
| 2 |
|
|---|
| 3 |
(defmethod slot-serialize-value ((slot xml-effective-slot-definition) value) |
|---|
| 4 |
(with-slots (serializer object-to-id) slot |
|---|
| 5 |
(when object-to-id |
|---|
| 6 |
(setf value (funcall object-to-id value))) |
|---|
| 7 |
(when serializer |
|---|
| 8 |
(setf value (funcall serializer value))))) |
|---|
| 9 |
|
|---|
| 10 |
(defvar *objects-written*) |
|---|
| 11 |
|
|---|
| 12 |
(defmacro with-xml-export* ((&key output indentation canonical) &body body) |
|---|
| 13 |
`(let ((*objects-written* (make-hash-table :test #'equal)) |
|---|
| 14 |
(cxml::*current-element* nil) |
|---|
| 15 |
(cxml::*sink* (cxml:make-character-stream-sink ,output |
|---|
| 16 |
:indentation ,indentation :canonical ,canonical))) |
|---|
| 17 |
,@body)) |
|---|
| 18 |
|
|---|
| 19 |
(defmacro with-xml-export (nil &body body) |
|---|
| 20 |
`(with-xml-export* (:output *standard-output* :indentation 1 :canonical nil) |
|---|
| 21 |
,@body)) |
|---|
| 22 |
|
|---|
| 23 |
(defun write-object-reference (class object unique-id-slot-name name) |
|---|
| 24 |
(let ((slotdef (find unique-id-slot-name (class-slots class) :key #'slot-definition-name))) |
|---|
| 25 |
(unless (xml-effective-slot-definition-attribute slotdef) |
|---|
| 26 |
(error "Slot ~A is not defined as :attribute slot and cannot be used as unique-id slot for class ~A" unique-id-slot-name (class-name class))) |
|---|
| 27 |
(sax:start-element cxml::*sink* nil nil name |
|---|
| 28 |
(list (sax:make-attribute :qname (cxml::string-rod (xml-effective-slot-definition-attribute slotdef)) |
|---|
| 29 |
:value (cxml::string-rod (slot-serialize-value slotdef (slot-value object unique-id-slot-name)))))) |
|---|
| 30 |
(sax:end-element cxml::*sink* nil nil name))) |
|---|
| 31 |
|
|---|
| 32 |
(defgeneric write-to-xml (object &key) |
|---|
| 33 |
(:documentation "Write OBJECT to XML stream") |
|---|
| 34 |
|
|---|
| 35 |
(:method ((object (eql nil)) &key)) |
|---|
| 36 |
|
|---|
| 37 |
(:method ((object list) &key (name (error "Can not serialize list to XML without an element name~%"))) |
|---|
| 38 |
(sax:start-element cxml::*sink* nil nil (cxml::string-rod name) nil) |
|---|
| 39 |
(dolist (obj object) |
|---|
| 40 |
(write-to-xml obj)) |
|---|
| 41 |
(sax:end-element cxml::*sink* nil nil (cxml::string-rod name))) |
|---|
| 42 |
|
|---|
| 43 |
(:method ((object string) &key (name (error "Can not serialize string ~A to XML without an element name." object))) |
|---|
| 44 |
(sax:start-element cxml::*sink* nil nil (cxml::string-rod name) nil) |
|---|
| 45 |
(sax:characters cxml::*sink* (cxml::string-rod object)) |
|---|
| 46 |
(sax:end-element cxml::*sink* nil nil (cxml::string-rod name))) |
|---|
| 47 |
|
|---|
| 48 |
(:method ((object standard-object) &key) |
|---|
| 49 |
(cxml:with-element (string-downcase (class-name (class-of object))) |
|---|
| 50 |
(dolist (slot (class-slots (class-of object))) |
|---|
| 51 |
(cxml:with-element (string-downcase (symbol-name (slot-definition-name slot))) |
|---|
| 52 |
(let ((value (slot-value object (slot-definition-name slot)))) |
|---|
| 53 |
(when value |
|---|
| 54 |
(cxml:text (handler-case |
|---|
| 55 |
(cxml::utf8-string-to-rod (princ-to-string value)) |
|---|
| 56 |
(error (e) |
|---|
| 57 |
(declare (ignore e)) |
|---|
| 58 |
(cxml::utf8-string-to-rod "[unprintable]")))))))))) |
|---|
| 59 |
|
|---|
| 60 |
(:method ((object xml-class) &key name) |
|---|
| 61 |
(xml-object-check-validity object) |
|---|
| 62 |
(let* ((class (class-of object)) |
|---|
| 63 |
(qname (cxml::string-rod (or name (xml-class-element class))))) |
|---|
| 64 |
|
|---|
| 65 |
;; If this object has been serialized to the XML stream, |
|---|
| 66 |
;; write a reference to the object and return. |
|---|
| 67 |
|
|---|
| 68 |
(with-slots (unique-id-slot) class |
|---|
| 69 |
(when unique-id-slot |
|---|
| 70 |
(if (gethash (slot-value object (first unique-id-slot)) *objects-written*) |
|---|
| 71 |
(progn |
|---|
| 72 |
(write-object-reference class object (first unique-id-slot) qname) |
|---|
| 73 |
(return-from write-to-xml)) |
|---|
| 74 |
(setf (gethash (slot-value object (first unique-id-slot)) *objects-written*) t)))) |
|---|
| 75 |
|
|---|
| 76 |
;; Object has not been written to the XML file or no |
|---|
| 77 |
;; unique-id-slot is defined for this class. |
|---|
| 78 |
|
|---|
| 79 |
(let* ((attr-slots (xml-class-attribute-slots class)) |
|---|
| 80 |
(elt-slots (xml-class-element-slots class)) |
|---|
| 81 |
(body-slot (xml-class-body-slot class)) |
|---|
| 82 |
;; attributes |
|---|
| 83 |
(attributes (loop for slot in attr-slots |
|---|
| 84 |
for name = (slot-definition-name slot) |
|---|
| 85 |
for attdef = (cxml::string-rod (xml-effective-slot-definition-attribute slot)) |
|---|
| 86 |
when (and (slot-boundp object name) |
|---|
| 87 |
(slot-value object name)) |
|---|
| 88 |
collect (sax:make-attribute |
|---|
| 89 |
:qname attdef |
|---|
| 90 |
:value |
|---|
| 91 |
(cxml::string-rod |
|---|
| 92 |
(slot-serialize-value slot (slot-value object name))))))) |
|---|
| 93 |
(sax:start-element cxml::*sink* nil nil qname attributes) |
|---|
| 94 |
|
|---|
| 95 |
;; elements |
|---|
| 96 |
(dolist (slot elt-slots) |
|---|
| 97 |
(let ((name (slot-definition-name slot)) |
|---|
| 98 |
(element-name (xml-effective-slot-definition-element slot))) |
|---|
| 99 |
(when (slot-boundp object name) |
|---|
| 100 |
(if (consp (slot-value object name)) |
|---|
| 101 |
(dolist (child (slot-value object name)) |
|---|
| 102 |
(if (typep (class-of child) 'xml-class) |
|---|
| 103 |
(write-to-xml child) |
|---|
| 104 |
(write-to-xml (slot-serialize-value slot child) :name element-name))) |
|---|
| 105 |
(let ((child (slot-value object name))) |
|---|
| 106 |
(if (typep (class-of child) 'xml-class) |
|---|
| 107 |
(write-to-xml child) |
|---|
| 108 |
(write-to-xml (slot-serialize-value slot child) :name element-name))))))) |
|---|
| 109 |
|
|---|
| 110 |
;; body slot |
|---|
| 111 |
(when body-slot |
|---|
| 112 |
(let ((name (slot-definition-name body-slot))) |
|---|
| 113 |
(when (slot-boundp object name) |
|---|
| 114 |
(sax:characters |
|---|
| 115 |
cxml::*sink* |
|---|
| 116 |
(cxml::string-rod |
|---|
| 117 |
(funcall (xml-effective-slot-definition-serializer body-slot) |
|---|
| 118 |
(slot-value object name))))))) |
|---|
| 119 |
|
|---|
| 120 |
(sax:end-element cxml::*sink* nil nil qname))))) |
|---|