2

I'm trying clojure (for the first time) for a simple project. I need to update an xml tree given a csv file. I'm reading the csv file line by line, extract some values, loop up a node given some values and insert a child node with another value.

This works fine the first time I insert an item. The second time I get a NullPointerException (without a trace). I lookup the root from the return value I get from insert-child and pass that root node to the next loop. Somehow the second insert fails on that root element. Anyone sees what's going wrong here? Or feedback in general on this code since this is my first attempt to write some Clojure.

(require 'clojure.string)
(require '[clojure.java.io :as io])
(require '[clojure.xml :as xml])
(require '[clojure.zip :as zip])
(require '[clojure.data.zip.xml :as zf])

(def business-object-config (xml/parse "BusinessObject.config"))
(def zipped (zip/xml-zip business-object-config ))

(defn sql-table-name [table-name]
  (second (re-matches #"(.*?)(Base|ExtensionBase|$)" table-name)))

(defn insert-sqlpropertyname-elem [loc name]
  (zip/root (zip/insert-child loc {:tag :SqlPropertyName :content [name]})))

(defn get-entity-node [table-name crm-name business-objects]
  (first (zf/xml-> business-objects :Entities
    :Entity [:CrmName (zf/text= (clojure.string/lower-case (sql-table-name table-name)))]
    :EntityItems
    :EntityItem [:CrmPropertyName (zf/text= (clojure.string/lower-case crm-name))])))

(defn process-line [line business-objects]
  (let [{crm-name 0 table-name 1 sql-name 6} (clojure.string/split line #";")
        node (get-entity-node table-name crm-name business-objects)]
    (insert-sqlpropertyname-elem node sql-name)))

(defn process-csv []
  (with-open
    [rdr (io/reader "input.csv")]
      (loop [lines (vec (take 5 (rest (line-seq rdr))))
             index (dec (count lines))
             boc zipped]
        (if (neg? index)
          boc
        (recur lines (dec index) (process-line (nth lines index) boc))))))

(spit "out.xml" (with-out-str (xml/emit (process-csv)) :pad true))
Jeroen
  • 557
  • 1
  • 7
  • 15

2 Answers2

0

the not-so-helpful NPE usually means you ran off the side of the tree somewhere.

i suspect you don't need the call to zip/root in insert-sqlpropertyname-elem.

Arthur Ulfeldt
  • 90,827
  • 27
  • 201
  • 284
  • The problem has to do with the return value of the insert-child function. If I don't call zip/root on the structure and do a query with the get-entity-node function it returns nil. If I do call zip/root I get the NPE. So the return value from insert-child is somehow a different data structure than the original zipper tree. So more precisely, how do I transform the data structure that is returned from insert-child to something that I can query on with xml-> ? – Jeroen May 31 '12 at 08:25
  • What about replacing `(recur lines (dec index) (process-line (nth lines index) boc))` with `(do (process-line (nth lines index) boc) (recur lines (dec index) boc))` – Ankur May 31 '12 at 12:14
0

Ok, I don't know if it's the right way, but if I call root on the result of insert-child and than call xml-zip on the root everything works fine. Don't know if it's best practice to zip the entire structure after every mutation?

Jeroen
  • 557
  • 1
  • 7
  • 15