diff --git a/pom.xml b/pom.xml index 5b967ac..7199704 100644 --- a/pom.xml +++ b/pom.xml @@ -7,6 +7,18 @@ org.example GGD 1.0-SNAPSHOT + + + + org.apache.maven.plugins + maven-compiler-plugin + + 7 + 7 + + + + org.apache.jena @@ -25,6 +37,17 @@ jena-fuseki-main 3.16.0 + + info.picocli + picocli + 4.5.1 + + + org.glassfish + javax.json + 1.1.4 + + diff --git a/src/main/java/Data/city-distances.json b/src/main/java/Data/city-distances.json new file mode 100644 index 0000000..957f72d --- /dev/null +++ b/src/main/java/Data/city-distances.json @@ -0,0 +1,28 @@ +{ + "desc" : "Distances between several cities, in kilometers.", + "updated" : "2014-02-04T18:50:45", + "uptodate": true, + "author" : null, + "cities" : { + "Brussels": [ + {"to": "London", "distance": 322}, + {"to": "Paris", "distance": 265}, + {"to": "Amsterdam", "distance": 173} + ], + "London": [ + {"to": "Brussels", "distance": 322}, + {"to": "Paris", "distance": 344}, + {"to": "Amsterdam", "distance": 358} + ], + "Paris": [ + {"to": "Brussels", "distance": 265}, + {"to": "London", "distance": 344}, + {"to": "Amsterdam", "distance": 431} + ], + "Amsterdam": [ + {"to": "Brussels", "distance": 173}, + {"to": "London", "distance": 358}, + {"to": "Paris", "distance": 431} + ] + } +} diff --git a/src/main/java/JSON2RDF/JSON2RDF.java b/src/main/java/JSON2RDF/JSON2RDF.java new file mode 100644 index 0000000..00022c2 --- /dev/null +++ b/src/main/java/JSON2RDF/JSON2RDF.java @@ -0,0 +1,67 @@ +package JSON2RDF; + +import java.io.BufferedReader; +import java.io.BufferedWriter; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.OutputStream; +import java.io.OutputStreamWriter; +import java.io.Reader; +import java.net.URI; +import java.nio.charset.Charset; +import java.nio.charset.StandardCharsets; + +import org.apache.jena.riot.system.StreamRDF; +import org.apache.jena.riot.system.StreamRDFLib; +import picocli.CommandLine; + +@CommandLine.Command(name = "json2rdf") +public class JSON2RDF { + private final InputStream jsonIn; + private final OutputStream rdfOut; + + @CommandLine.Parameters(paramLabel = "base", index = "0", description = "Base URI of the RDF output data\nExample: https://localhost/") + private URI baseURI; + + @CommandLine.Option(names = { "--input-charset" }, description = "Input charset (default: ${DEFAULT-VALUE})") + private Charset inputCharset = StandardCharsets.UTF_8; + + @CommandLine.Option(names = { "--output-charset" }, description = "Output charset (default: ${DEFAULT-VALUE})") + private Charset outputCharset = StandardCharsets.UTF_8; + + public static void main(String[] args) throws IOException + { + JSON2RDF json2rdf = new JSON2RDF(System.in, System.out); + + try + { + CommandLine.ParseResult parseResult = new CommandLine(json2rdf).parseArgs(args); + if (!CommandLine.printHelpIfRequested(parseResult)) json2rdf.convert(); + } + catch (CommandLine.ParameterException ex) + { // command line arguments could not be parsed + System.err.println(ex.getMessage()); + ex.getCommandLine().usage(System.err); + } + } + + public JSON2RDF(InputStream csvIn, OutputStream rdfOut) + { + this.jsonIn = csvIn; + this.rdfOut = rdfOut; + } + + public void convert() throws IOException + { + if (jsonIn.available() == 0) throw new IllegalStateException("JSON input not provided"); + + try (Reader reader = new BufferedReader(new InputStreamReader(jsonIn, inputCharset))) + { + StreamRDF rdfStream = StreamRDFLib.writer(new BufferedWriter(new OutputStreamWriter(rdfOut, outputCharset))); + new JsonStreamRDFWriter(reader, rdfStream, baseURI.toString()).convert(); + } + } + +} + diff --git a/src/main/java/JSON2RDF/JsonStreamRDFWriter.java b/src/main/java/JSON2RDF/JsonStreamRDFWriter.java new file mode 100644 index 0000000..d75e8df --- /dev/null +++ b/src/main/java/JSON2RDF/JsonStreamRDFWriter.java @@ -0,0 +1,124 @@ +package JSON2RDF; + +import java.io.InputStream; +import java.io.Reader; +import java.util.ArrayDeque; +import java.util.Deque; +import java.util.HashMap; +import java.util.Map; +import javax.json.Json; +import javax.json.stream.JsonParser; +import org.apache.jena.datatypes.xsd.XSDDatatype; +import org.apache.jena.graph.Node; +import org.apache.jena.graph.NodeFactory; +import org.apache.jena.graph.Triple; +import org.apache.jena.riot.system.IRIResolver; +import org.apache.jena.riot.system.StreamRDF; + + +public class JsonStreamRDFWriter +{ + + private final JsonParser parser; + private final StreamRDF rdfStream; + private final IRIResolver iriResolver; + + public JsonStreamRDFWriter(Reader reader, StreamRDF rdfStream, String baseURI) + { + this(Json.createParser(reader), rdfStream, baseURI); + } + + public JsonStreamRDFWriter(InputStream is, StreamRDF rdfStream, String baseURI) + { + this(Json.createParser(is), rdfStream, baseURI); + } + + public JsonStreamRDFWriter(JsonParser parser, StreamRDF rdfStream, String baseURI) + { + this.parser = parser; + this.rdfStream = rdfStream; + this.iriResolver = IRIResolver.create(baseURI); + } + + public void convert() + { + getStreamRDF().start(); + + write(getParser(), getStreamRDF(), getIRIResolver()); + + getStreamRDF().finish(); + } + + public static void write(JsonParser parser, StreamRDF rdfStream, IRIResolver iriResolver) + { + Deque subjectStack = new ArrayDeque<>(); + Map arrayProperties = new HashMap<>(); + + Node property = null; + while (parser.hasNext()) + { + JsonParser.Event event = parser.next(); + + switch (event) + { + case START_ARRAY: + if (!subjectStack.isEmpty() && property != null) arrayProperties.put(subjectStack.getLast(), property); + break; + case END_ARRAY: + if (!subjectStack.isEmpty()) arrayProperties.remove(subjectStack.getLast()); + break; + case START_OBJECT: + Node subject = NodeFactory.createBlankNode(); + // add triple with current array property, if any + if (property != null && !subjectStack.isEmpty()) rdfStream.triple(new Triple(subjectStack.getLast(), property, subject)); + subjectStack.addLast(subject); + break; + case END_OBJECT: + subjectStack.removeLast(); + // restore previous array property, if there was any + if (!subjectStack.isEmpty() && arrayProperties.containsKey(subjectStack.getLast())) property = arrayProperties.get(subjectStack.getLast()); + break; + case VALUE_FALSE: + rdfStream.triple(new Triple(subjectStack.getLast(), property, NodeFactory.createLiteralByValue(Boolean.FALSE, XSDDatatype.XSDboolean))); + break; + case VALUE_TRUE: + rdfStream.triple(new Triple(subjectStack.getLast(), property, NodeFactory.createLiteralByValue(Boolean.TRUE, XSDDatatype.XSDboolean))); + break; + case KEY_NAME: + property = NodeFactory.createURI(iriResolver.resolveToString("#" + parser.getString())); + break; + case VALUE_STRING: + if (property != null) rdfStream.triple(new Triple(subjectStack.getLast(), property, NodeFactory.createLiteral(parser.getString()))); + break; + case VALUE_NUMBER: + try + { + rdfStream.triple(new Triple(subjectStack.getLast(), property,NodeFactory.createLiteralByValue(Integer.valueOf(parser.getString()), XSDDatatype.XSDint))); + } + catch (NumberFormatException ex) + { + rdfStream.triple(new Triple(subjectStack.getLast(), property,NodeFactory.createLiteralByValue(Float.valueOf(parser.getString()), XSDDatatype.XSDfloat))); + } + break; + case VALUE_NULL: + break; + } + } + } + + protected JsonParser getParser() + { + return parser; + } + + protected StreamRDF getStreamRDF() + { + return rdfStream; + } + + protected IRIResolver getIRIResolver() + { + return iriResolver; + } + +} \ No newline at end of file diff --git a/src/main/java/main.java b/src/main/java/main.java index 1ec8312..6aa1378 100644 --- a/src/main/java/main.java +++ b/src/main/java/main.java @@ -1,4 +1,5 @@ +import JSON2RDF.JSON2RDF; import org.apache.jena.rdf.model.*; import org.apache.jena.vocabulary.*; @@ -44,5 +45,6 @@ public class main { System.out.println(" ."); } } + }