Last active
October 16, 2016 22:29
-
-
Save RedRoserade/1b76a4c4d81e3c0df51af9fcc56a357c to your computer and use it in GitHub Desktop.
JSON to SwiftyJSON-enabled classes
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
'use strict'; | |
const indentStr = ' '; | |
const swiftTypeToSwiftyJsonProp = { | |
'String': 'string', | |
'Boolean': 'boolean', | |
'Double': 'double' | |
} | |
function indent(line, level) { | |
if (typeof level === 'undefined') { | |
level = 1; | |
} | |
return Array.from({ length: level }).reduce((acc, l) => acc + indentStr, '') + line; | |
} | |
function indentLines(text, level) { | |
if (typeof level === 'undefined') { | |
level = 1; | |
} | |
return text.split('\n') | |
.map(l => indent(l, level)) | |
.join('\n'); | |
} | |
function typeOf(v, options) { | |
switch (typeof v) { | |
case "string": | |
if (/^\d{4}-\d{2}-\d{2}$/.test(v)) { | |
return { type: "Date", childTypes: options.childTypes, format: 'yyyy-MM-dd' }; | |
} | |
if (/^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}$/.test(v)) { | |
return { type: "Date", childTypes: options.childTypes, format: "yyyy-MM-dd'T'HH:mm:ss" }; | |
} | |
return { type: "String", childTypes: options.childTypes }; | |
case "boolean": | |
return { type: "Bool", childTypes: options.childTypes }; | |
case "number": | |
return { type: "Double", childTypes: options.childTypes }; | |
} | |
if (Array.isArray(v)) { | |
return parseArray(v, options); | |
} | |
if (v === null) { | |
return { type: 'Any?' }; | |
} | |
return parseObject(v, options); | |
} | |
function parseArray(arr, options) { | |
let firstItem = arr.filter(i => i !== null)[0]; | |
let result = typeOf(firstItem, options); | |
return { type: `[${result.type}]`, childTypes: result.childTypes, parentType: 'Array' }; | |
} | |
function parseObject(obj, options) { | |
let name = options.propName[0].toUpperCase() + options.propName.substr(1); | |
if (name.toLowerCase().indexOf("collection") !== -1 || | |
options.parentType === 'Array') { | |
name = name.replace(/collection$/i, 'Item'); | |
} | |
// Check if we have a new type. | |
if (Array.isArray(options.childTypes) && !options.childTypes.find(t => t.name === name)) { | |
let newTypeProps = Object.keys(obj) | |
.map(k => { | |
return { | |
name: k, | |
type: typeOf(obj[k], { propName: k, childTypes: options.childTypes }) | |
}; | |
}); | |
options.childTypes.push({ name: name, props: newTypeProps }); | |
return { | |
type: name, | |
childTypes: options.childTypes | |
}; | |
} | |
return { type: name, childTypes: options.childTypes }; | |
} | |
function makeInit(spec) { | |
let lines = [ | |
'init(' | |
]; | |
let constructorArgs = spec.props.map(p => `${p.name}: ${p.type.type}`).join(', '); | |
lines[0] += constructorArgs; | |
lines[0] += ') {'; | |
spec.props | |
.map(p => indent(`self.${p.name} = ${p.name}`)) | |
.forEach(c => lines.push(c)); | |
lines.push('}'); | |
lines.push(''); | |
lines.push('// MARK: - Init JSON'); | |
lines.push(''); | |
lines.push('convenience init(fromJson json: JSON) {') | |
lines.push(indent('self.init(')); | |
let jsonInitArgs = spec.props | |
.map(p => { | |
let arg = `${p.name}: ` //json[${p.name}].???` | |
let typeName = p.type.type; | |
if (typeName === 'Any?') { | |
// Unknown... | |
arg += `json["${p.name}"] /* #warning: Unknown type */`; | |
} else if (typeName[0] === '[') { | |
// Array | |
let arrayTypeArg = typeName.replace(/\[|\]/g, ''); | |
if (arrayTypeArg === 'Any') { | |
arg += `json["${p.name}"].array! /* #warning: Unknown type */`; | |
} else { | |
arg += `json["${p.name}"].array!.map(` | |
let jsonProp = swiftTypeToSwiftyJsonProp[arrayTypeArg]; | |
if (jsonProp !== undefined) { | |
// Primitive type | |
arg += `{$0.${jsonProp}!})`; | |
} else { | |
// Complex type | |
arg += `${arrayTypeArg}.init)`; | |
} | |
} | |
} else if (typeName == 'Date') { | |
// Initialize with a closure | |
let closureSrc = `{let df = DateFormatter(); df.dateFormat = "${p.type.format}"; return df.date(from: json["${p.name}"].string!)}()` | |
arg += closureSrc | |
} else { | |
// Others (object, primitive) | |
let jsonProp = swiftTypeToSwiftyJsonProp[typeName]; | |
if (jsonProp !== undefined) { | |
// Primitive type | |
arg += `json["${p.name}"].${jsonProp}!`; | |
} else { | |
// Complex type | |
arg += `${typeName}(fromJson: json["${p.name}"])`; | |
} | |
} | |
return arg; | |
}) | |
.join(',\n'); | |
lines.push(indentLines(jsonInitArgs, 2)); | |
lines.push(indent(')')); | |
lines.push('}'); | |
return lines.join('\n'); | |
} | |
function classToString(spec) { | |
let lines = [ | |
`class ${spec.name} {` | |
]; | |
lines.push(''); | |
lines.push(indent('// MARK: - Init')); | |
lines.push(''); | |
// init | |
let initCode = makeInit(spec); | |
lines.push(indentLines(initCode)); | |
lines.push(''); | |
lines.push(indent('// MARK: - Properties')); | |
lines.push(''); | |
// Props | |
spec.props | |
.map(p => { | |
let line = indent(`var ${p.name}: ${p.type.type}`); | |
return line; | |
}) | |
.forEach(l => lines.push(l)); | |
lines.push('}'); | |
return lines.join('\n'); | |
} | |
function jsonToClasses(jsonStr, rootClassName) { | |
let obj = JSON.parse(jsonStr); | |
let spec = typeOf(obj, { propName: rootClassName, childTypes: [] }); | |
let classes = spec.childTypes.map(classToString) | |
classes.unshift('import Foundation') | |
classes.unshift('import SwiftyJSON') | |
let results = classes.join('\n\n'); | |
return results; | |
} | |
// For use in a terminal: | |
// node json-to-swiftyjson.js <path to json> <optional root class name, defaults to "Root"> | |
let path = process.argv[2]; | |
let rootClassName = process.argv[3] || "Root"; | |
if (path === undefined) { | |
console.log("No source path specified."); | |
process.exit(1); | |
} | |
let fs = require('fs'); | |
let contents = fs.readFileSync(path, 'utf8'); | |
let classes = jsonToClasses(contents, rootClassName); | |
console.log(classes); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment