diff --git a/README.md b/README.md index 38edc4f..182b352 100644 --- a/README.md +++ b/README.md @@ -8,65 +8,67 @@ Simple command line utility to convert CSV files to EDN. Assumes the first row o ### Leiningen/Boot -[csv2edn "0.1.0"] +[csv2edn "0.1.4"] ### Clojure CLI/deps.edn -csv2edn {:mvn/version "0.1.0"} +csv2edn {:mvn/version "0.1.4"} ### Gradle -compile 'csv2edn:csv2edn:0.1.0' +compile 'csv2edn:csv2edn:0.1.4' ### Maven csv2edn csv2edn - 0.1.0 + 0.1.4 ## Usage: as a standalone commandline tool To run from the command line: - $ java -jar csv2edn-0.1.0-standalone.jar [options] + $ java -jar csv2edn-0.1.4-standalone.jar [options] ### Options Where options are: - -h, --help Print this message and exit. - -i, --input INPUT The path name of the CSV file to be read. + -f, --format FORMAT :edn Format the ouput as `FORMAT` + (expected to be either `edn` or `json`, defaults to `edn`) + -h, --help Print this message and exit. + -i, --input INPUT The path name of the CSV file to be read. If no input file is specified, input will be read from standard input. - -o, --output OUTPUT The path name of the EDN file to be written. + -o, --output OUTPUT The path name of the EDN file to be written. If no output file is specified, output will be written to standard output. - -s, --separator SEPARATOR The character to treat as a separator in the - CSV file. If not specified, comma will be used by default. + -s, --separator SEPARATOR , The character to treat as a separator in + the CSV file ### Examples The simplest possible use is to simply use this in a pipeline: $ cat path/to/file.csv |\ - java -jar csv2edn-0.1.0-standalone.jar > path/to/file.edn + java -jar csv2edn-0.1.4-standalone.jar > path/to/file.edn Exactly the same behaviour can be achieved by specifying input and output paths: - $ java -jar csv2edn-0.1.0-standalone.jar \ + $ java -jar csv2edn-0.1.4-standalone.jar \ -i path/to/file.csv -o path/to/file.edn or - $ java -jar csv2edn-0.1.0-standalone.jar \ + $ java -jar csv2edn-0.1.4-standalone.jar \ --input path/to/file.csv --output path/to/file.edn ## Usage: as a library To use this package as a library in your own code: - (:require [csv2edn.csv2edn :refer [csv->edn *options*]]) + (:require [csv2edn.csv2edn :refer [csv->edn csv->json *options*]]) And then pass either filenames or a reader and writer respectively to the function `cvs->edn`, thus: @@ -95,6 +97,8 @@ Or else pass the separator character you want to use as a third argument: (cvs->edn "path/to/data.csv" "path/to/data.edn" \|) +If you wish to produce JSON, call the function `csv->json` exactly as +`csv->edn` called above. ### Bugs diff --git a/project.clj b/project.clj index e691b56..2157956 100644 --- a/project.clj +++ b/project.clj @@ -1,10 +1,11 @@ -(defproject csv2edn "0.1.4-SNAPSHOT" +(defproject csv2edn "0.1.4" :description "Simple command line utility to convert CSV files to EDN." :url "https://github.com/simon-brooke/csv2edn" :license {:name "GPL-2.0-or-later WITH Classpath-exception-2.0" :url "https://www.gnu.org/licenses/old-licenses/gpl-2.0.en.html"} :dependencies [[org.clojure/clojure "1.8.0"] [org.clojure/data.csv "0.1.4"] + [org.clojure/data.json "0.2.7"] [org.clojure/tools.cli "0.4.2"]] :main ^:skip-aot csv2edn.core :target-path "target/%s" diff --git a/src/csv2edn/core.clj b/src/csv2edn/core.clj index f35586c..d2c1e0b 100644 --- a/src/csv2edn/core.clj +++ b/src/csv2edn/core.clj @@ -3,13 +3,18 @@ csv2edn.core (:require [clojure.java.io :as io] [clojure.tools.cli :refer [parse-opts]] - [csv2edn.csv2edn :refer [csv->edn *options*]]) + [csv2edn.csv2edn :refer [csv->edn csv->json *options*]]) (:gen-class)) (def cli-options "Command line argument specification." - [["-h" "--help" "Print this message and exit."] + [["-f" "--format FORMAT" "Format the ouput as `FORMAT` + (expected to be either `edn` or `json`, defaults to `edn`)" + :default :edn + :parse-fn #(keyword %) + :validate [#(#{:edn :json} %) "Format should be one of `edn`, `json`."]] + ["-h" "--help" "Print this message and exit."] ["-i" "--input INPUT" "The path name of the CSV file to be read. If no input file is specified, input will be read from standard input." :validate [#(and @@ -19,7 +24,8 @@ ["-o" "--output OUTPUT" "The path name of the EDN file to be written. If no output file is specified, output will be written to standard output."] ["-s" "--separator SEPARATOR" - "The character to treat as a separator in the CSV file" + "The character to treat as a separator in + the CSV file" :parse-fn #(first (str %)) :default \,] ]) @@ -37,11 +43,9 @@ (apply str (interpose "; " (:errors args))) "")) (if-not (or (:errors args) (:help (:options args))) (binding [*options* (:options args)] - (doall - (csv->edn - (or - (:input *options*) - (io/reader *in*)) - (or (:output *options*) (io/writer *out*))) - ))))) + (let [from (or (:input *options*) (io/reader *in*)) + to (or (:output *options*) (io/writer *out*))] + (case (:format *options*) + :json (doall (csv->json from to)) + :edn (doall (csv->edn from to) ))))))) diff --git a/src/csv2edn/csv2edn.clj b/src/csv2edn/csv2edn.clj index 58b9091..4e29b7d 100644 --- a/src/csv2edn/csv2edn.clj +++ b/src/csv2edn/csv2edn.clj @@ -1,6 +1,7 @@ (ns ^{:doc "Conversion from CVS to EDN." :author "Simon Brooke"} csv2edn.csv2edn (:require [clojure.data.csv :as csv] + [clojure.data.json :as json] [clojure.java.io :as io] [clojure.pprint :refer [pprint]] [clojure.string :refer [lower-case]])) @@ -27,20 +28,32 @@ ([from] (csv->edn from nil)) ([from to] (csv->edn from to (:separator *options*))) ([from to sep] - (let [data (with-open [reader (if (string? from)(io/reader from) from)] - (doall - (csv/read-csv - reader - :separator (first (str sep))))) - headers (map #(keyword (lower-case %)) (first data))] - (let [result (map - #(zipmap headers (map maybe-read %)) - (rest data))] - (if to - (with-open [writer (if (string? to) (io/writer to) to)] - (pprint - result - writer))) - result)))) - + (let [data (with-open [reader (if (string? from)(io/reader from) from)] + (doall + (csv/read-csv + reader + :separator (first (str sep))))) + headers (map #(keyword (lower-case %)) (first data)) + result (map + #(zipmap headers (map maybe-read %)) + (rest data))] + (if to + (with-open [writer (if (string? to) (io/writer to) to)] + (pprint + result + writer))) + result))) +(defn csv->json + "Read a CSV stream from the reader or filename `from`, and write corresponding + JSON to the reader or filname `to`, using the separator character `sep`. Assume + column headers are in row 1. If `from` is `nil` or not supplied, do not write. + It `sep` is not supplied, take the `:separator` value from `*options*`. Returns + a (possibly-lazy) sequence of maps." + ([from] (csv->json from nil)) + ([from to] (csv->json from to (:separator *options*))) + ([from to sep] + (let [result (json/write-str (csv->edn from nil sep))] + (if to + (spit to result)) + result)))