commit a7ed9d461a00b27b24e0a368dde7fcff03a39569 Author: AJ ONeal Date: Mon Jan 30 13:18:18 2012 -0700 added json2yaml diff --git a/README.md b/README.md new file mode 100644 index 0000000..1a3f9f0 --- /dev/null +++ b/README.md @@ -0,0 +1,73 @@ +JSON to YAML +=== + +The purpose of this utility is to pretty-print JSON in the human-readable YAML object notation +(ignore the misnomer, it's not a markup language at all) + +You see, JSON is a proper subset of YAML, The difference is that YAML can use whitespace instead of syntax, which is more human-readable. +Also, YAML supports comments. + +So, for all the times you want to turn JSON int YAML (YML): + + { + "foo": "bar", + "baz": [ + "qux", + "quxx" + ], + "corge": null, + "grault": 1, + "garply": true, + "waldo": "false", + "fred": "undefined" + } + +becomes + + --- + foo: "bar" + baz: + - "qux" + - "quxx" + corge: null + grault: 1 + garply: true + waldo: "false" + fred: "undefined" + +Usage +--- + +Specify a file: + + json2yaml ./example.json + + yaml2json ./example.kml | json2yaml + +Or pipe from stdin: + + curl -s http://foobar3000.com/echo/echo.json | json2yaml + + wget -qO- http://foobar3000.com/echo/echo.json | json2yaml + +Or require: + + (function () { + "use strict"; + + var YAML = require('json2yaml') + , ymlText + ; + + ymlText = YAML.stringify({ + "foo": "bar" + , "baz": "corge" + }); + + console.log(ymlText); + }()); + +Installation +--- + + npm install -g json2yaml diff --git a/cli.js b/cli.js new file mode 100644 index 0000000..0084ee8 --- /dev/null +++ b/cli.js @@ -0,0 +1,93 @@ +#!/usr/bin/env node +(function () { + "use strict"; + + var fs = require('fs') + , filename = process.argv[2] + , YAML = require('./index') + ; + + /* + * + * Begin real handler + * + */ + + function printUsage() { + console.warn("Usages:"); + console.warn("json2yaml example.json"); + console.warn("cat example.json | json2yaml"); + } + + function handleInput(err, text) { + var data + ; + + if (err) { + printUsage(); + return; + } + + data = JSON.parse(text); + console.info(YAML.stringify(data, null, ' ')); + } + + /* + * + * End real handler + * + */ + + readInput(handleInput, filename); + + // + // this could (and probably should) be its own module + // + function readInput(cb, filename) { + + function readFile() { + fs.readFile(filename, 'utf8', function (err, text) { + if (err) { + console.error("[ERROR] couldn't read from '" + filename + "':"); + console.error(err.message); + return; + } + + cb(err, text); + }); + } + + function readStdin() { + var text = '' + , timeoutToken + , stdin = process.stdin + ; + + stdin.resume(); + + // how to tell piping vs waiting for user input? + timeoutToken = setTimeout(function () { + cb(new Error('no stdin data')); + stdin.pause(); + }, 1000); + + stdin.on('data', function (chunk) { + clearTimeout(timeoutToken); + text += chunk; + }); + + stdin.on('end', function () { + cb(null, text); + }); + } + + if (filename) { + readFile(); + } + else { + readStdin(); + } + + } + +}()); diff --git a/example.json b/example.json new file mode 100644 index 0000000..ff217aa --- /dev/null +++ b/example.json @@ -0,0 +1,12 @@ +{ + "foo": "bar", + "baz": [ + "qux", + "quxx" + ], + "corge": null, + "grault": 1, + "garply": true, + "waldo": "false", + "fred": "undefined" +} diff --git a/example.yml b/example.yml new file mode 100644 index 0000000..cd88ea5 --- /dev/null +++ b/example.yml @@ -0,0 +1,10 @@ +--- + foo: bar + baz: + - qux + - quxx + corge: null + grault: 1 + garply: true + waldo: "false" + fred: undefined diff --git a/index.js b/index.js new file mode 100644 index 0000000..ea2e8ab --- /dev/null +++ b/index.js @@ -0,0 +1,93 @@ +(function () { + "use strict"; + + var typeOf = require('remedial').typeOf + ; + + function stringify(data) { + var handlers + , indentLevel = '' + ; + + handlers = { + "undefined": function () { + // objects will not have `undefined` converted to `null` + // as this may have unintended consequences + // For arrays, however, this behavior seems appropriate + return 'null'; + } + , "null": function () { + return 'null'; + } + , "number": function (x) { + return x; + } + , "boolean": function (x) { + return x ? 'true' : 'false'; + } + , "string": function (x) { + // to avoid the string "true" being confused with the + // the literal `true`, we always wrap strings in quotes + return JSON.stringify(x); + } + , "array": function (x) { + var output = '' + ; + + indentLevel = indentLevel.replace(/$/, ' '); + x.forEach(function (y) { + // TODO how should `undefined` be handled? + var handler = handlers[typeOf(y)] + ; + + if (!handler) { + throw new Error('what the crap: ' + typeOf(y)); + } + + output += '\n' + indentLevel + '- ' + handler(y); + + }); + indentLevel = indentLevel.replace(/ /, ''); + + return output; + } + , "object": function (x) { + var output = '' + ; + + indentLevel = indentLevel.replace(/$/, ' '); + Object.keys(x).forEach(function (k) { + var val = x[k] + , handler = handlers[typeOf(val)] + ; + + if ('undefined' === typeof val) { + // the user should do + // delete obj.key + // and not + // obj.key = undefined + // but we'll error on the side of caution + return; + } + + if (!handler) { + throw new Error('what the crap: ' + typeOf(val)); + } + + output += '\n' + indentLevel + k + ': ' + handler(val); + }); + indentLevel = indentLevel.replace(/ /, ''); + + return output; + } + , "function": function () { + // TODO this should throw or otherwise be ignored + return '[object Function]'; + } + }; + + return '---' + handlers[typeOf(data)](data); + } + + module.exports.stringify = stringify; +}()); diff --git a/package.json b/package.json new file mode 100644 index 0000000..332bef3 --- /dev/null +++ b/package.json @@ -0,0 +1,21 @@ +{ + "author": "AJ ONeal (http://coolaj86.info)", + "name": "json2yaml", + "description": "A commandline utility to convert JSON to YAML / YML", + "keywords": ["yml", "yaml", "json", "cli", "util"], + "version": "1.0.0", + "main": "index.js", + "bin": { + "json2yaml": "./cli.js", + "json2yml": "./cli.js" + }, + "engines": { + "node": ">= 0.2.0" + }, + "test": ["a", "b", "c"], + "dependencies": { + "remedial": "1.x" + }, + "devDependencies": {}, + "preferGlobal": true +} diff --git a/test.sh b/test.sh new file mode 100755 index 0000000..ba7e381 --- /dev/null +++ b/test.sh @@ -0,0 +1,13 @@ +#!/bin/bash + +# npm install -g yaml2json +node cli.js tests/object.json | yaml2json > /dev/null +node cli.js tests/array.json | yaml2json > /dev/null +# These tests would probably fail and seem a moot point to me +# Why use YAML for literal values? It's general used for config +# files will multiple teirs of data +#node cli.js tests/string.json | yaml2json +#node cli.js tests/number.json | yaml2json +#node cli.js tests/boolean.json | yaml2json +#node cli.js tests/null.json | yaml2json +echo "Passed if no errors are listed above (and yaml2json is installed)" diff --git a/tests/array.json b/tests/array.json new file mode 100644 index 0000000..d38d42e --- /dev/null +++ b/tests/array.json @@ -0,0 +1,22 @@ +[ + "baz", + "qux", + "quxx", + null, + true, + { + "foo": "bar", + "corge": null, + "grault": 1, + "garply": true, + "waldo": "false", + "fred": "undefined" + }, + [ + "hello", + "world" + ], + 42, + [ + ] +] diff --git a/tests/object.json b/tests/object.json new file mode 100644 index 0000000..66b956c --- /dev/null +++ b/tests/object.json @@ -0,0 +1,17 @@ +{ + "foo": "bar", + "baz": [ + "qux", + "quxx" + ], + "corge": { + "grault": 1, + "garply": true, + "waldo": "false", + "fred": null + }, + "empty": { + }, + "hello": "world", + "answer": 42 +}