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(" .");
         }
     }
+
 }