Kaz Kylheku
10/19/2015 5:42:00 AM
On 2015-10-19, schatzer.johann@gmail.com <schatzer.johann@gmail.com> wrote:
> This is s-xml output of an xml file
>
> '(:|bookstore|
> ((:|book| :|category| "cooking")
> ((:|title| :|lang| "en")
> "Everyday Italian")
> (:|author|
> "Giada De Laurentiis")
> (:|year|
> "2005")
> (:|price|
> "30.00"))
> ((:|book| :|category| "children")
> ((:|title| :|lang| "en")
> "Harry Potter")
> (:|author|
> "J K. Rowling"))))
>
> I am struggling contructing a recursive function
> converting the above into tis tree
>
> '("bookstore"
> ("book (category cooking)"
> ("title (lang en)"
> ("Everyday Italian"))
> ("author"
> ("Giada De Laurentiis"))
> ("year"
> ("2005"))
> ("price"
> ("30.00")))
> ("book (category children)"
> ("title (lang en)"
> ("Harry Potter"))
> ("author"
> ("J K. Rowling"))))
>
> Could you please show how one would systematically do this?
Systematically, we begin by specifying the rewrite rules, and
assuring ourselves that the specification is right.
Let us denote the basic transformation with curly braces { }
and a transformation applies individually to the elements of
a list using *{ }:
From the example, it seems we have two main cases:
{ (:|symbol| rest...) } -> ("symbol" *{ rest... })
{ ((:|symbol| attributes ...) rest...) ("symbol (attributes ...)" *{ rest ...})
For completeness, we can probably add:
{ () } -> ()
Now these rest... items can just be strings, so we have
to treat those:
{ "str" } -> "str"
We need to know how to generate these strings. The symbol :|abc| can be
converted to "abc" via the symbol-name function, or more generally
via princ-to-string. The latter handles the attribute lists also,
but we have to massage their shape. An even better idea is to use
the format function:
(format nil "~a" :|foo|) -> "foo"
(format nil "~a ~a" :|foo| '(:|bar| :|xyzzy|)) -> "foo (bar xyzzy)"
So now we have the ingredients:
(defun transform (sxml)
(cond
((null sxml) nil)
((atom sxml) ;; implies stringp
(format nil "~a" sxml))
((symbolp (car sxml))
(destructuring-bind (sym . rest) sxml
`(,(format nil "~a" sym)
,@(mapcar #'transform rest))))
(t (destructuring-bind ((sym . rest-attrs) . rest) sxml
`(,(format nil "~a ~a" sym rest-attrs)
,@(mapcar #'transform rest))))))
Test:
(transform '(:|bookstore|
((:|book| :|category| "cooking")
((:|title| :|lang| "en")
"Everyday Italian")
(:|author|
"Giada De Laurentiis")
(:|year|
"2005")
(:|price|
"30.00"))
((:|book| :|category| "children")
((:|title| :|lang| "en")
"Harry Potter")
(:|author|
"J K. Rowling")))))
-->
("bookstore"
("book (category cooking)" ("title (lang en)" "Everyday Italian")
("author" "Giada De Laurentiis") ("year" "2005") ("price" "30.00"))
("book (category children)" ("title (lang en)" "Harry Potter")
("author" "J K. Rowling")))
Oops, not quite. We are missing the rule for wrapping the element
contents in an extra list layer. It seems that a hack for achieving
this is to wrap an extra list around string items:
(defun transform (sxml)
(cond
((null sxml) nil)
((atom sxml) ;; implies stringp
(list (format nil "~a" sxml)))
((symbolp (car sxml))
(destructuring-bind (sym . rest) sxml
`(,(format nil "~a" sym)
,@(mapcar #'transform rest))))
(t (destructuring-bind ((sym . rest-attrs) . rest) sxml
`(,(format nil "~a ~a" sym rest-attrs)
,@(mapcar #'transform rest))))))
I now get:
("bookstore"
("book (category cooking)" ("title (lang en)" ("Everyday Italian"))
("author" ("Giada De Laurentiis")) ("year" ("2005")) ("price" ("30.00")))
("book (category children)" ("title (lang en)" ("Harry Potter"))
("author" ("J K. Rowling"))))