-
Notifications
You must be signed in to change notification settings - Fork 2
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
* Introduce jsonmerge-cli * Fix issue with Java 8 * Additional JUnits * Add Maven Shade plugin for uber JAR creation * Apply naming standard, exclude json-smart from uber jar
- Loading branch information
1 parent
5b7a56e
commit 9806e7d
Showing
13 changed files
with
632 additions
and
9 deletions.
There are no files selected for viewing
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
/target/ |
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,97 @@ | ||
<!-- | ||
Copyright 2022 obvj.net | ||
Licensed under the Apache License, Version 2.0 (the "License"); | ||
you may not use this file except in compliance with the License. | ||
You may obtain a copy of the License at | ||
http://www.apache.org/licenses/LICENSE-2.0 | ||
Unless required by applicable law or agreed to in writing, software | ||
distributed under the License is distributed on an "AS IS" BASIS, | ||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
See the License for the specific language governing permissions and | ||
limitations under the License. | ||
--> | ||
|
||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"> | ||
<modelVersion>4.0.0</modelVersion> | ||
|
||
<parent> | ||
<groupId>net.obvj</groupId> | ||
<artifactId>jsonmerge</artifactId> | ||
<version>1.1.1-SNAPSHOT</version> | ||
</parent> | ||
|
||
<artifactId>jsonmerge-cli</artifactId> | ||
<name>JSON Merge CLI</name> | ||
<description> | ||
CLI tool for merging JSON files | ||
</description> | ||
|
||
<dependencies> | ||
|
||
<dependency> | ||
<groupId>net.obvj</groupId> | ||
<artifactId>jsonmerge-core</artifactId> | ||
<version>${project.version}</version> | ||
<exclusions> | ||
<exclusion> | ||
<groupId>net.minidev</groupId> | ||
<artifactId>json-smart</artifactId> | ||
</exclusion> | ||
</exclusions> | ||
</dependency> | ||
|
||
<dependency> | ||
<groupId>com.google.code.gson</groupId> | ||
<artifactId>gson</artifactId> | ||
<version>${gson.version}</version> | ||
</dependency> | ||
|
||
<dependency> | ||
<groupId>info.picocli</groupId> | ||
<artifactId>picocli</artifactId> | ||
<version>4.6.3</version> | ||
</dependency> | ||
|
||
<dependency> | ||
<groupId>org.slf4j</groupId> | ||
<artifactId>slf4j-simple</artifactId> | ||
<version>${slf4j.version}</version> | ||
</dependency> | ||
|
||
</dependencies> | ||
|
||
<build> | ||
<plugins> | ||
|
||
<plugin> | ||
<groupId>org.apache.maven.plugins</groupId> | ||
<artifactId>maven-shade-plugin</artifactId> | ||
<version>3.4.0</version> | ||
<executions> | ||
<execution> | ||
<phase>package</phase> | ||
<goals> | ||
<goal>shade</goal> | ||
</goals> | ||
<configuration> | ||
<transformers> | ||
<transformer implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer"> | ||
<manifestEntries> | ||
<Main-Class>net.obvj.jsonmerge.cli.Main</Main-Class> | ||
<X-Compile-Source-JDK>${maven.compile.source}</X-Compile-Source-JDK> | ||
<X-Compile-Target-JDK>${maven.compile.target}</X-Compile-Target-JDK> | ||
<Application-Version>${project.version}</Application-Version> | ||
</manifestEntries> | ||
</transformer> | ||
</transformers> | ||
</configuration> | ||
</execution> | ||
</executions> | ||
</plugin> | ||
</plugins> | ||
</build> | ||
|
||
</project> |
78 changes: 78 additions & 0 deletions
78
jsonmerge-cli/src/main/java/net/obvj/jsonmerge/cli/ApplicationManifest.java
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,78 @@ | ||
/* | ||
* Copyright 2022 obvj.net | ||
* | ||
* Licensed under the Apache License, Version 2.0 (the "License"); | ||
* you may not use this file except in compliance with the License. | ||
* You may obtain a copy of the License at | ||
* | ||
* http://www.apache.org/licenses/LICENSE-2.0 | ||
* | ||
* Unless required by applicable law or agreed to in writing, software | ||
* distributed under the License is distributed on an "AS IS" BASIS, | ||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
* See the License for the specific language governing permissions and | ||
* limitations under the License. | ||
*/ | ||
|
||
package net.obvj.jsonmerge.cli; | ||
|
||
import java.io.InputStream; | ||
import java.util.jar.Attributes; | ||
import java.util.jar.Manifest; | ||
|
||
/** | ||
* A class that holds application information retrieved from the {@code MANIFEST.MF} file. | ||
* | ||
* @author oswaldo.bapvic.jr | ||
* @since 1.2.0 | ||
*/ | ||
public class ApplicationManifest | ||
{ | ||
/** | ||
* The application name; a string in the format {@code <artifactId>-<version>.jar} | ||
*/ | ||
public static final String COMMAND_NAME = parseCommandName(loadManifest()); | ||
|
||
private static Manifest loadManifest() | ||
{ | ||
return loadManifest("META-INF/MANIFEST.MF"); | ||
} | ||
|
||
/** | ||
* Loads a {@link Manifest} from the specified file name, or an "empty" object if the | ||
* specified file name is not found or not accessible. | ||
* | ||
* @param fileName the manifest file name to be loaded | ||
* @return a {@link Manifest} from the specified file name | ||
*/ | ||
static Manifest loadManifest(String fileName) | ||
{ | ||
try (InputStream input = ApplicationManifest.class.getClassLoader() | ||
.getResourceAsStream(fileName)) | ||
{ | ||
return new Manifest(input); | ||
} | ||
catch (Exception exception) | ||
{ | ||
return new Manifest(); | ||
} | ||
} | ||
|
||
static String parseCommandName(Manifest manifest) | ||
{ | ||
return String.format("jsonmerge-cli-%s.jar", | ||
defaultIfNull(manifest.getMainAttributes(), "Application-Version", "<version>")); | ||
} | ||
|
||
private static String defaultIfNull(Attributes attributes, String key, String defaultValue) | ||
{ | ||
String value = attributes.getValue(key); | ||
return value != null ? value : defaultValue; | ||
} | ||
|
||
private ApplicationManifest() | ||
{ | ||
throw new UnsupportedOperationException("Instantiation not allowed"); | ||
} | ||
|
||
} |
170 changes: 170 additions & 0 deletions
170
jsonmerge-cli/src/main/java/net/obvj/jsonmerge/cli/Main.java
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,170 @@ | ||
/* | ||
* Copyright 2022 obvj.net | ||
* | ||
* Licensed under the Apache License, Version 2.0 (the "License"); | ||
* you may not use this file except in compliance with the License. | ||
* You may obtain a copy of the License at | ||
* | ||
* http://www.apache.org/licenses/LICENSE-2.0 | ||
* | ||
* Unless required by applicable law or agreed to in writing, software | ||
* distributed under the License is distributed on an "AS IS" BASIS, | ||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
* See the License for the specific language governing permissions and | ||
* limitations under the License. | ||
*/ | ||
|
||
package net.obvj.jsonmerge.cli; | ||
|
||
import static net.obvj.jsonmerge.cli.ApplicationManifest.*; | ||
|
||
import java.io.IOException; | ||
import java.nio.file.Files; | ||
import java.nio.file.Path; | ||
import java.util.Collections; | ||
import java.util.Map; | ||
import java.util.Map.Entry; | ||
import java.util.concurrent.Callable; | ||
|
||
import org.slf4j.Logger; | ||
import org.slf4j.LoggerFactory; | ||
|
||
import com.google.gson.GsonBuilder; | ||
import com.google.gson.JsonObject; | ||
|
||
import net.obvj.jsonmerge.JsonMergeOption; | ||
import net.obvj.jsonmerge.JsonMerger; | ||
import net.obvj.jsonmerge.provider.JsonProvider; | ||
import net.obvj.jsonmerge.provider.JsonProviderFactory; | ||
import picocli.CommandLine; | ||
import picocli.CommandLine.Command; | ||
import picocli.CommandLine.Option; | ||
import picocli.CommandLine.Parameters; | ||
|
||
/** | ||
* A command line tool to merge JSON files. | ||
* <p> | ||
* Usage: | ||
* | ||
* <pre> | ||
* {@code Main [-hp] -t <target> [-d <exp=key>]... <FILE1> <FILE2>} | ||
* </pre> | ||
* | ||
* @author oswaldo.bapvic.jr | ||
* @since 1.2.0 | ||
*/ | ||
@Command(separator = " ", | ||
usageHelpWidth = 90, | ||
parameterListHeading = "\nParameters:\n\n", | ||
optionListHeading = "\nOptions:\n\n") | ||
public class Main implements Callable<Integer> | ||
{ | ||
|
||
@Parameters(index = "0", | ||
paramLabel = "<FILE1>", | ||
description = "The first file to merge") | ||
private Path file1; | ||
|
||
@Parameters(index = "1", | ||
paramLabel = "<FILE2>", | ||
description = "The second file to merge") | ||
private Path file2; | ||
|
||
@Option(names = { "-t", "--target" }, | ||
description = "The target file name (default: result.json)", | ||
defaultValue = "result.json", | ||
required = true) | ||
private Path target; | ||
|
||
@Option(names = { "-d", "--distinct" }, | ||
paramLabel = "<exp=key>", | ||
description = { "Defines one or more distinct keys inside a child path", | ||
"For example: -d $.agents=name", | ||
" -d $.files=id,version" }) | ||
private Map<String, String> distinctKeys = Collections.emptyMap(); | ||
|
||
@Option(names = { "-p", "--pretty" }, | ||
description = "Generates a well-formatted result file") | ||
private boolean prettyPrinting = false; | ||
|
||
@Option(names = { "-h", "--help" }, | ||
usageHelp = true, | ||
description = "Displays a help message") | ||
private boolean helpRequested = false; | ||
|
||
private JsonProvider<JsonObject> provider = JsonProviderFactory.instance() | ||
.getByType(JsonObject.class); | ||
|
||
private GsonBuilder gsonBuilder = new GsonBuilder(); | ||
|
||
private Logger log = LoggerFactory.getLogger(Main.class); | ||
|
||
/** | ||
* Executes the CLI logic. | ||
* | ||
* @return the exit code to be handled by the CLI framework | ||
*/ | ||
@Override | ||
public Integer call() throws Exception | ||
{ | ||
String result = parseAndMerge(); | ||
|
||
log.info("Generating output file {} ...", target); | ||
Files.write(target, result.getBytes()); | ||
|
||
log.info("Success"); | ||
return 0; | ||
} | ||
|
||
String parseAndMerge() throws IOException | ||
{ | ||
if (prettyPrinting) gsonBuilder.setPrettyPrinting(); | ||
|
||
log.info("Parsing {} ...", file1); | ||
JsonObject json1 = provider.parse(Files.newInputStream(file1)); | ||
|
||
log.info("Parsing {} ...", file2); | ||
JsonObject json2 = provider.parse(Files.newInputStream(file2)); | ||
|
||
JsonMergeOption[] mergeOptions = parseJsonMergeOptions(); | ||
JsonObject result = new JsonMerger<>(JsonObject.class) | ||
.merge(json1, json2, mergeOptions); | ||
|
||
return toString(result); | ||
} | ||
|
||
JsonMergeOption[] parseJsonMergeOptions() | ||
{ | ||
return distinctKeys.entrySet().stream() | ||
.map(this::parseJsonMergeOption) | ||
.toArray(JsonMergeOption[]::new); | ||
} | ||
|
||
private JsonMergeOption parseJsonMergeOption(Entry<String, String> entry) | ||
{ | ||
JsonMergeOption mergeOption = JsonMergeOption.onPath(entry.getKey()) | ||
.findObjectsIdentifiedBy(split(entry.getValue())) | ||
.thenDoADeepMerge(); | ||
log.info("Parsed {}", mergeOption); | ||
return mergeOption; | ||
} | ||
|
||
private String[] split(String string) | ||
{ | ||
return string.split(","); | ||
} | ||
|
||
private String toString(JsonObject json) | ||
{ | ||
return gsonBuilder.create().toJson(json); | ||
} | ||
|
||
/** | ||
* @param args the command line arguments to parse | ||
*/ | ||
public static void main(String[] args) | ||
{ | ||
System.exit(new CommandLine(new Main()).setCommandName(COMMAND_NAME).execute(args)); | ||
} | ||
|
||
} |
Oops, something went wrong.