Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Conversion from YAML to JSON #2

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
49 changes: 45 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
# json2yaml

Gradle plugin for JSON to YAML tranformation
Gradle plugin for JSON to YAML or YAML to JSON tranformation

### Step 1
## Basic Usage

### Install the plugin

Apply the plugin

Expand All @@ -12,11 +14,50 @@ plugins {
}
```

### Step 2
### Original Tasks (single file conversion)

Set input and output file
The original plugin version offered a simple model where a single input and output file could be set for conversion.

For JSON to YAML:
```
json2yaml.inputFile = project.file('sample.json')
json2yaml.outputFile = project.file('output.yaml')
```

For YAML to JSON:
```
yaml2json.inputFile = project.file('sample.yaml')
yaml2json.outputFile = project.file('output.json')
```

### Enhanced Tasks (multiple file conversion)

A newer set of enhanced tasks has been added which allows

- multiple files to be converted
- adds support for Gradle incremental builds
- allows either a file collection to be supplied, with conversion outputs flattened, or a file tree which preserves the original directory structure
- allows file extension mapping to be specified using task configuration options. If the input file extension matches, it will be swapped with the configured output extension


For JSON to YAML - using file collection:
```
task incremental(type: dev.castocolina.gradle.plugin.JsonToYaml) {
outDir = file("${buildDir}/generated")
// collection of files - converted files will appear directly under outDir
sourceFiles = layout.files('tst.json', 'src/resources/jsonschema/tst2.json')
}
```

For YAML to JSON - using file tree:
```
YamlToJson {
outDir = file("${buildDir}/generated")
// file tree - converted files will use same sub-directory structure as originals
sourceFiles = fileTree(dir: "src", include: "resources/jsonschema/*.yaml")
// Extension mapping (values shown are actually the defaults).
yamlExt = ".yaml"
jsonExt = ".json"

}
```
5 changes: 4 additions & 1 deletion build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -27,13 +27,16 @@ ext {
}

dependencies {
implementation 'org.codehaus.groovy:groovy-all:3.0.5'
implementation 'org.codehaus.groovy:groovy-yaml:3.0.5'

implementation "com.fasterxml.jackson.dataformat:jackson-dataformat-yaml:${jacksonVer}"
implementation "com.fasterxml.jackson.datatype:jackson-datatype-jsr310:${jacksonVer}"
// Use the awesome Spock testing and specification framework
testImplementation 'org.spockframework:spock-core:1.3-groovy-2.5'
}

version = "1.0.0-SNAPSHOT"
version = "1.1.0-SNAPSHOT"

pluginBundle {
website = 'https://github.com/castocolina/json2yaml'
Expand Down
181 changes: 166 additions & 15 deletions src/main/groovy/dev/castocolina/gradle/plugin/Json2YamlPlugin.groovy
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
/*
* This Groovy source file was generated by the Gradle 'init' task.
* Various flavours of JSON <> YAML converstion task
* Note: I am definitely more of a Java coder than a Groovy one, and also on something of a Gradle learning process here
* So feel free to critique and suggest improvements!
*/
package dev.castocolina.gradle.plugin

Expand All @@ -10,30 +12,50 @@ import com.fasterxml.jackson.databind.ObjectMapper
import com.fasterxml.jackson.dataformat.yaml.YAMLFactory
import com.fasterxml.jackson.dataformat.yaml.YAMLGenerator.Feature

import groovy.yaml.YamlSlurper
import com.fasterxml.jackson.core.JsonFactory

import org.gradle.api.file.FileCollection
import org.gradle.api.file.FileTree
import org.gradle.api.DefaultTask
import org.gradle.api.tasks.Input
import org.gradle.api.tasks.InputFiles
import org.gradle.api.tasks.Internal
import org.gradle.api.tasks.OutputDirectory
import org.gradle.api.tasks.TaskAction
import org.gradle.api.tasks.SkipWhenEmpty
import org.gradle.api.tasks.incremental.IncrementalTaskInputs

public class Json2YamlPluginExtension {
File inputFile = new File('sample.json')
File outputFile = new File('build/tmp/docs.yaml')
Boolean minimizeQuote = Boolean.TRUE
Boolean deleteTargetFirst = Boolean.TRUE
}

public class Yaml2JsonPluginExtension {
File inputFile = new File('sample.yaml')
File outputFile = new File('build/tmp/docs.json')
Boolean deleteTargetFirst = Boolean.TRUE
}

/**
* A simple 'hello world' plugin.
* Main class to register plugin tasks
*/
public class Json2YamlPlugin implements Plugin<Project> {
public void apply(Project project) {
def extension = project.extensions.create('json2yaml', Json2YamlPluginExtension)

// Register a task
// Register task from JSON to YAML - from code originally forked
def extToJson = project.extensions.create('json2yaml', Json2YamlPluginExtension)

project.tasks.register("json2yaml") {
def inJson, ouYaml, minQuote, delTarget

doFirst {
inJson = extension.inputFile
ouYaml = extension.outputFile
minQuote = extension.minimizeQuote
delTarget = extension.deleteTargetFirst
inJson = extToJson.inputFile
ouYaml = extToJson.outputFile
minQuote = extToJson.minimizeQuote
delTarget = extToJson.deleteTargetFirst

println "Transform input: " + inJson.absolutePath
println "Transform output: " + ouYaml.absolutePath
Expand All @@ -43,17 +65,146 @@ public class Json2YamlPlugin implements Plugin<Project> {
}

doLast {
def jsonText = inJson.text
def jSonMap = new JsonSlurper().parseText(jsonText)
def factory = new YAMLFactory()
if (minQuote){
factory.enable(Feature.MINIMIZE_QUOTES)
JsonToYaml.doIt(inJson, ouYaml, minQuote)
println("File converted!!!")
}

}

// Register task from YAML to JSON - following same approach as code originally forked
def extToYaml = project.extensions.create('yaml2json', Yaml2JsonPluginExtension)

project.tasks.register("yaml2json") {
def inYaml, ouJson, delTarget

doFirst {
inYaml = extToYaml.inputFile
ouJson = extToYaml.outputFile
delTarget = extToYaml.deleteTargetFirst

println "Transform input: " + inYaml.absolutePath
println "Transform output: " + ouJson.absolutePath
if (delTarget){
println "Delete target first: " + ouJson.delete()
}
def mapper = new ObjectMapper(factory)
mapper.writeValue(ouYaml, jSonMap)
}

doLast {
YamlToJson.doIt(inYaml, ouJson)
println("File converted!!!")
}

}

// Alternate approach to register custom tasks for converrsion. This allows use of the
// injection of config properties and support for incremental build handling
project.tasks.register("JsonToYaml", JsonToYaml)
project.tasks.register("YamlToJson", YamlToJson)
}
}

// Newer classes to provide conversion tasks with incremental build support

public abstract class ConverterTask extends DefaultTask {
@OutputDirectory
File outDir = project.file("${project.buildDir}/generated")

@InputFiles
@SkipWhenEmpty
FileCollection sourceFiles

@Input
String yamlExt = ".yaml" // configurable default extension for YAML files

@Input
String jsonExt = ".json" // configurable default extension for JSON files


@Internal
def convertMap = [:]

void initConversionMap(String extFrom, String extTo) {
if (sourceFiles instanceof FileTree) {
// For file trees, we will try and preserve structure on output
FileTree tree = (FileTree) sourceFiles
tree.visit {element ->
if (element.file.isFile()) {
convertMap.put(element.file, swapExtension(element.relativePath.toString(), extFrom, extTo))
}
}
} else {
// For simple collections, output will be flattened
sourceFiles.each {File file ->
if (file.isFile()) {
convertMap.put(file, swapExtension(file.name, extFrom, extTo))
}
}
}
}

String swapExtension(String path, String extFrom, String extTo) {
return (path.endsWith(extFrom)) ? path.take(path.lastIndexOf('.')) + extTo
: path + extTo
}

void convertAll() {
convertMap.each{entry -> convert(entry.key, new File(outDir, entry.value))}
}

abstract void convert(File fin, File fout)
}

public class YamlToJson extends ConverterTask {

@TaskAction
void perform() {
initConversionMap(yamlExt, jsonExt)
convertAll()
}

void convert(File fin, File fout) {
doIt(fin, fout)
}

static void doIt (File fin, File fout) {
def yamlText = fin.text
def yamlMap = new YamlSlurper().parseText(yamlText)
def factory = new JsonFactory()
def mapper = new ObjectMapper(factory)

fout.parentFile.mkdirs()
mapper.writeValue(fout, yamlMap)
}
}


public class JsonToYaml extends ConverterTask {

@Input
boolean minimizeQuote = true

@TaskAction
void perform() {
initConversionMap(jsonExt, yamlExt)
convertAll()
}

void convert(File fin, File fout) {
doIt(fin, fout, minimizeQuote)
}

static void doIt (File fin, File fout, boolean minQuote) {
def jsonText = fin.text
def jSonMap = new JsonSlurper().parseText(jsonText)
def factory = new YAMLFactory()
if (minQuote){
factory.enable(Feature.MINIMIZE_QUOTES)
}
def mapper = new ObjectMapper(factory)

fout.parentFile.mkdirs()
mapper.writeValue(fout, jSonMap)
}
}


Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,9 @@ public class Json2YamlPluginTest extends Specification {
project.plugins.apply("dev.castocolina.json2yaml")

then:
project.tasks.findByName("json2yaml") != null

(project.tasks.findByName("json2yaml") != null &&
project.tasks.findByName("yaml2json") != null &&
project.tasks.findByName("JsonToYaml") != null &&
project.tasks.findByName("YamlToJson") != null)
}
}