Index: /trunk/src/org/openstreetmap/josm/actions/ExtensionFileFilter.java
===================================================================
--- /trunk/src/org/openstreetmap/josm/actions/ExtensionFileFilter.java	(revision 15423)
+++ /trunk/src/org/openstreetmap/josm/actions/ExtensionFileFilter.java	(revision 15424)
@@ -21,4 +21,5 @@
 import org.openstreetmap.josm.gui.io.importexport.FileExporter;
 import org.openstreetmap.josm.gui.io.importexport.FileImporter;
+import org.openstreetmap.josm.gui.io.importexport.GeoJSONImporter;
 import org.openstreetmap.josm.gui.io.importexport.GpxImporter;
 import org.openstreetmap.josm.gui.io.importexport.JpgImporter;
@@ -64,4 +65,5 @@
                 OsmImporter.class,
                 OsmChangeImporter.class,
+                GeoJSONImporter.class,
                 GpxImporter.class,
                 NMEAImporter.class,
Index: /trunk/src/org/openstreetmap/josm/actions/downloadtasks/DownloadGeoJsonTask.java
===================================================================
--- /trunk/src/org/openstreetmap/josm/actions/downloadtasks/DownloadGeoJsonTask.java	(revision 15424)
+++ /trunk/src/org/openstreetmap/josm/actions/downloadtasks/DownloadGeoJsonTask.java	(revision 15424)
@@ -0,0 +1,71 @@
+// License: GPL. For details, see LICENSE file.
+package org.openstreetmap.josm.actions.downloadtasks;
+
+import static org.openstreetmap.josm.tools.I18n.tr;
+
+import java.util.Optional;
+import java.util.concurrent.Future;
+
+import org.openstreetmap.josm.data.Bounds;
+import org.openstreetmap.josm.data.osm.DataSet;
+import org.openstreetmap.josm.gui.MainApplication;
+import org.openstreetmap.josm.gui.layer.OsmDataLayer;
+import org.openstreetmap.josm.gui.progress.ProgressMonitor;
+import org.openstreetmap.josm.io.GeoJSONServerReader;
+import org.openstreetmap.josm.tools.Utils;
+
+/**
+ * GeoJson download task.
+ * @author Omar Vega Ramos &lt;ovruni@riseup.net&gt;
+ * @since 15424
+ */
+public class DownloadGeoJsonTask extends DownloadOsmTask {
+
+    private static final String PATTERN_COMPRESS = "https?://.*/(.*\\.(json|geojson)(\\.(gz|xz|bz2?|zip))?)";
+
+    @Override
+    public String[] getPatterns() {
+        return new String[]{PATTERN_COMPRESS};
+    }
+
+    @Override
+    public String getTitle() {
+        return tr("Download GeoJSON");
+    }
+
+    @Override
+    public Future<?> download(DownloadParams settings, Bounds downloadArea, ProgressMonitor progressMonitor) {
+        return null;
+    }
+
+    @Override
+    public Future<?> loadUrl(DownloadParams settings, String url, ProgressMonitor progressMonitor) {
+        downloadTask = new InternalDownloadTask(settings, url, progressMonitor);
+        return MainApplication.worker.submit(downloadTask);
+    }
+
+    class InternalDownloadTask extends DownloadTask {
+
+        private final String url;
+
+        InternalDownloadTask(DownloadParams settings, String url, ProgressMonitor progressMonitor) {
+            super(settings, new GeoJSONServerReader(url), progressMonitor);
+            this.url = url;
+        }
+
+        @Override
+        protected String generateLayerName() {
+            return Optional.of(url.substring(url.lastIndexOf('/')+1))
+                .filter(it -> !Utils.isStripEmpty(it))
+                .orElse(super.generateLayerName());
+        }
+
+        @Override
+        protected OsmDataLayer createNewLayer(final DataSet dataSet, final Optional<String> layerName) {
+            if (layerName.filter(Utils::isStripEmpty).isPresent()) {
+                throw new IllegalArgumentException("Blank layer name!");
+            }
+            return new OsmDataLayer(dataSet, layerName.orElseGet(this::generateLayerName), null);
+        }
+    }
+}
Index: /trunk/src/org/openstreetmap/josm/gui/io/importexport/GeoJSONImporter.java
===================================================================
--- /trunk/src/org/openstreetmap/josm/gui/io/importexport/GeoJSONImporter.java	(revision 15424)
+++ /trunk/src/org/openstreetmap/josm/gui/io/importexport/GeoJSONImporter.java	(revision 15424)
@@ -0,0 +1,77 @@
+// License: GPL. For details, see LICENSE file.
+package org.openstreetmap.josm.gui.io.importexport;
+
+import static org.openstreetmap.josm.tools.I18n.tr;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.Arrays;
+
+import javax.swing.JOptionPane;
+
+import org.openstreetmap.josm.actions.ExtensionFileFilter;
+import org.openstreetmap.josm.data.osm.DataSet;
+import org.openstreetmap.josm.gui.MainApplication;
+import org.openstreetmap.josm.gui.layer.OsmDataLayer;
+import org.openstreetmap.josm.gui.progress.NullProgressMonitor;
+import org.openstreetmap.josm.gui.progress.ProgressMonitor;
+import org.openstreetmap.josm.gui.util.GuiHelper;
+import org.openstreetmap.josm.io.CachedFile;
+import org.openstreetmap.josm.io.Compression;
+import org.openstreetmap.josm.io.GeoJSONReader;
+import org.openstreetmap.josm.io.IllegalDataException;
+import org.openstreetmap.josm.tools.Logging;
+
+/**
+ * GeoJSON file importer.
+ * @author Ian Dees &lt;ian.dees@gmail.com&gt;
+ * @author matthieun &lt;https://github.com/matthieun&gt;
+ * @since 15424
+ */
+public class GeoJSONImporter extends FileImporter {
+
+    private static final ExtensionFileFilter FILE_FILTER = ExtensionFileFilter.newFilterWithArchiveExtensions(
+        "geojson,json", "geojson", tr("GeoJSON file") + " (*.geojson, *.geojson.gz, *.geojson.bz2, *.geojson.xz, *.geojson.zip, *.json)",
+        ExtensionFileFilter.AddArchiveExtension.NONE, Arrays.asList("gz", "bz", "bz2", "xz", "zip"));
+
+    /**
+     * Constructs a new GeoJSON File importer with an extension filter for .json and .geojson
+     */
+    public GeoJSONImporter() {
+        super(FILE_FILTER);
+    }
+
+    @Override
+    public void importData(final File file, final ProgressMonitor progressMonitor) {
+        progressMonitor.beginTask(tr("Loading json file…"));
+        progressMonitor.setTicksCount(2);
+        Logging.info("Parsing GeoJSON: {0}", file.getAbsolutePath());
+        try (InputStream fileInputStream = Compression.getUncompressedFileInputStream(file)) {
+            DataSet data = GeoJSONReader.parseDataSet(fileInputStream, progressMonitor);
+            progressMonitor.worked(1);
+            MainApplication.getLayerManager().addLayer(new OsmDataLayer(data, file.getName(), file));
+        } catch (final Exception e) {
+            Logging.error("Error while reading json file!");
+            Logging.error(e);
+            GuiHelper.runInEDT(() -> JOptionPane.showMessageDialog(
+                null, tr("Error loading geojson file {0}", file.getAbsolutePath()), tr("Error"), JOptionPane.WARNING_MESSAGE));
+        } finally {
+            progressMonitor.finishTask();
+        }
+    }
+
+    /**
+     * Parse GeoJSON dataset.
+     * @param source geojson file
+     * @return GeoJSON dataset
+     * @throws IOException in case of I/O error
+     * @throws IllegalDataException if an error was found while parsing the data from the source
+     */
+    public DataSet parseDataSet(final String source) throws IOException, IllegalDataException {
+        try (CachedFile cf = new CachedFile(source)) {
+            InputStream fileInputStream = Compression.getUncompressedFileInputStream(cf.getFile());
+            return GeoJSONReader.parseDataSet(fileInputStream, NullProgressMonitor.INSTANCE);
+        }
+    }
+}
Index: /trunk/src/org/openstreetmap/josm/io/GeoJSONReader.java
===================================================================
--- /trunk/src/org/openstreetmap/josm/io/GeoJSONReader.java	(revision 15424)
+++ /trunk/src/org/openstreetmap/josm/io/GeoJSONReader.java	(revision 15424)
@@ -0,0 +1,319 @@
+// License: GPL. For details, see LICENSE file.
+package org.openstreetmap.josm.io;
+
+import static org.openstreetmap.josm.tools.I18n.tr;
+
+import java.io.InputStream;
+import java.util.List;
+import java.util.Map;
+import java.util.Optional;
+import java.util.TreeMap;
+import java.util.stream.Collectors;
+
+import javax.json.Json;
+import javax.json.JsonArray;
+import javax.json.JsonObject;
+import javax.json.JsonString;
+import javax.json.JsonStructure;
+import javax.json.JsonValue;
+import javax.json.stream.JsonParser;
+import javax.json.stream.JsonParser.Event;
+
+import org.openstreetmap.josm.data.coor.LatLon;
+import org.openstreetmap.josm.data.osm.DataSet;
+import org.openstreetmap.josm.data.osm.Node;
+import org.openstreetmap.josm.data.osm.OsmPrimitive;
+import org.openstreetmap.josm.data.osm.Relation;
+import org.openstreetmap.josm.data.osm.RelationMember;
+import org.openstreetmap.josm.data.osm.Way;
+import org.openstreetmap.josm.gui.progress.NullProgressMonitor;
+import org.openstreetmap.josm.gui.progress.ProgressMonitor;
+import org.openstreetmap.josm.tools.Logging;
+
+/**
+ * Reader that reads GeoJSON files. See <a href="https://tools.ietf.org/html/rfc7946">RFC7946</a> for more information.
+ * @since 15424
+ */
+public class GeoJSONReader extends AbstractReader {
+
+    private static final String COORDINATES = "coordinates";
+    private static final String FEATURES = "features";
+    private static final String PROPERTIES = "properties";
+    private static final String GEOMETRY = "geometry";
+    private static final String TYPE = "type";
+    private JsonParser parser;
+
+    GeoJSONReader() {
+        // Restricts visibility
+    }
+
+    private void setParser(final JsonParser parser) {
+        this.parser = parser;
+    }
+
+    private void parse() {
+        while (parser.hasNext()) {
+            Event event = parser.next();
+            if (event == Event.START_OBJECT) {
+                parseRoot(parser.getObject());
+            }
+        }
+        parser.close();
+    }
+
+    private void parseRoot(final JsonObject object) {
+        switch (object.getString(TYPE)) {
+            case "FeatureCollection":
+                parseFeatureCollection(object.getJsonArray(FEATURES));
+                break;
+            case "Feature":
+                parseFeature(object);
+                break;
+            case "GeometryCollection":
+                parseGeometryCollection(null, object);
+                break;
+            default:
+                parseGeometry(null, object);
+        }
+    }
+
+    private void parseFeatureCollection(final JsonArray features) {
+        for (JsonValue feature : features) {
+            if (feature instanceof JsonObject) {
+                JsonObject item = (JsonObject) feature;
+                parseFeature(item);
+            }
+        }
+    }
+
+    private void parseFeature(final JsonObject feature) {
+        JsonValue geometry = feature.get(GEOMETRY);
+        if (geometry != null && geometry.getValueType().equals(JsonValue.ValueType.OBJECT)) {
+            parseGeometry(feature, geometry.asJsonObject());
+        } else {
+            JsonValue properties = feature.get(PROPERTIES);
+            if (properties != null && properties.getValueType().equals(JsonValue.ValueType.OBJECT)) {
+                parseNonGeometryFeature(feature, properties.asJsonObject());
+            } else {
+                Logging.warn(tr("Relation/non-geometry feature without properties found: {0}", feature));
+            }
+        }
+    }
+
+    private void parseNonGeometryFeature(final JsonObject feature, final JsonObject properties) {
+        // get relation type
+        JsonValue type = properties.get(TYPE);
+        if (type == null || properties.getValueType().equals(JsonValue.ValueType.STRING)) {
+            Logging.warn(tr("Relation/non-geometry feature without type found: {0}", feature));
+            return;
+        }
+
+        // create misc. non-geometry feature
+        final Relation relation = new Relation();
+        relation.put(TYPE, type.toString());
+        fillTagsFromFeature(feature, relation);
+        getDataSet().addPrimitive(relation);
+    }
+
+    private void parseGeometryCollection(final JsonObject feature, final JsonObject geometry) {
+        JsonArray geometries = geometry.getJsonArray("geometries");
+        for (JsonValue jsonValue : geometries) {
+            parseGeometry(feature, jsonValue.asJsonObject());
+        }
+    }
+
+    private void parseGeometry(final JsonObject feature, final JsonObject geometry) {
+        if (geometry == null) {
+            parseNullGeometry(feature);
+            return;
+        }
+
+        switch (geometry.getString(TYPE)) {
+            case "Point":
+                parsePoint(feature, geometry.getJsonArray(COORDINATES));
+                break;
+            case "MultiPoint":
+                parseMultiPoint(feature, geometry);
+                break;
+            case "LineString":
+                parseLineString(feature, geometry.getJsonArray(COORDINATES));
+                break;
+            case "MultiLineString":
+                parseMultiLineString(feature, geometry);
+                break;
+            case "Polygon":
+                parsePolygon(feature, geometry.getJsonArray(COORDINATES));
+                break;
+            case "MultiPolygon":
+                parseMultiPolygon(feature, geometry);
+                break;
+            case "GeometryCollection":
+                parseGeometryCollection(feature, geometry);
+                break;
+            default:
+                parseUnknown(geometry);
+        }
+    }
+
+    private void parsePoint(final JsonObject feature, final JsonArray coordinates) {
+        double lat = coordinates.getJsonNumber(1).doubleValue();
+        double lon = coordinates.getJsonNumber(0).doubleValue();
+        Node node = createNode(lat, lon);
+        fillTagsFromFeature(feature, node);
+    }
+
+    private void parseMultiPoint(final JsonObject feature, final JsonObject geometry) {
+        JsonArray coordinates = geometry.getJsonArray(COORDINATES);
+        for (JsonValue coordinate : coordinates) {
+            parsePoint(feature, coordinate.asJsonArray());
+        }
+    }
+
+    private void parseLineString(final JsonObject feature, final JsonArray coordinates) {
+        if (coordinates.isEmpty()) {
+            return;
+        }
+        createWay(coordinates, false)
+            .ifPresent(way -> fillTagsFromFeature(feature, way));
+    }
+
+    private void parseMultiLineString(final JsonObject feature, final JsonObject geometry) {
+        JsonArray coordinates = geometry.getJsonArray(COORDINATES);
+        for (JsonValue coordinate : coordinates) {
+            parseLineString(feature, coordinate.asJsonArray());
+        }
+    }
+
+    private void parsePolygon(final JsonObject feature, final JsonArray coordinates) {
+        if (coordinates.size() == 1) {
+            createWay(coordinates.getJsonArray(0), true)
+                .ifPresent(way -> fillTagsFromFeature(feature, way));
+        } else if (coordinates.size() > 1) {
+            // create multipolygon
+            final Relation multipolygon = new Relation();
+            multipolygon.put(TYPE, "multipolygon");
+            createWay(coordinates.getJsonArray(0), true)
+                .ifPresent(way -> multipolygon.addMember(new RelationMember("outer", way)));
+
+            for (JsonValue interiorRing : coordinates.subList(1, coordinates.size())) {
+                createWay(interiorRing.asJsonArray(), true)
+                    .ifPresent(way -> multipolygon.addMember(new RelationMember("inner", way)));
+            }
+
+            fillTagsFromFeature(feature, multipolygon);
+            getDataSet().addPrimitive(multipolygon);
+        }
+    }
+
+    private void parseMultiPolygon(final JsonObject feature, final JsonObject geometry) {
+        JsonArray coordinates = geometry.getJsonArray(COORDINATES);
+        for (JsonValue coordinate : coordinates) {
+            parsePolygon(feature, coordinate.asJsonArray());
+        }
+    }
+
+    private Node createNode(final double lat, final double lon) {
+        final Node node = new Node(new LatLon(lat, lon));
+        getDataSet().addPrimitive(node);
+        return node;
+    }
+
+    private Optional<Way> createWay(final JsonArray coordinates, final boolean autoClose) {
+        if (coordinates.isEmpty()) {
+            return Optional.empty();
+        }
+
+        final List<LatLon> latlons = coordinates.stream().map(coordinate -> {
+            final JsonArray jsonValues = coordinate.asJsonArray();
+            return new LatLon(
+                jsonValues.getJsonNumber(1).doubleValue(),
+                jsonValues.getJsonNumber(0).doubleValue()
+            );
+        }).collect(Collectors.toList());
+
+        final int size = latlons.size();
+        final boolean doAutoclose;
+        if (size > 1) {
+            if (latlons.get(0).equals(latlons.get(size - 1))) {
+                // Remove last coordinate, but later add first node to the end
+                latlons.remove(size - 1);
+                doAutoclose = true;
+            } else {
+                doAutoclose = autoClose;
+            }
+        } else {
+            doAutoclose = false;
+        }
+
+        final Way way = new Way();
+        way.setNodes(latlons.stream().map(Node::new).collect(Collectors.toList()));
+        if (doAutoclose) {
+            way.addNode(way.getNode(0));
+        }
+
+        way.getNodes().stream().distinct().forEach(it -> getDataSet().addPrimitive(it));
+        getDataSet().addPrimitive(way);
+
+        return Optional.of(way);
+    }
+
+    private void fillTagsFromFeature(final JsonObject feature, final OsmPrimitive primitive) {
+        if (feature != null) {
+            primitive.setKeys(getTags(feature));
+        }
+    }
+
+    private void parseUnknown(final JsonObject object) {
+        Logging.warn(tr("Unknown json object found {0}", object));
+    }
+
+    private void parseNullGeometry(JsonObject feature) {
+        Logging.warn(tr("Geometry of feature {0} is null", feature));
+    }
+
+    private Map<String, String> getTags(final JsonObject feature) {
+        final Map<String, String> tags = new TreeMap<>();
+
+        if (feature.containsKey(PROPERTIES) && !feature.isNull(PROPERTIES)) {
+            JsonValue properties = feature.get(PROPERTIES);
+            if (properties != null && JsonValue.ValueType.OBJECT.equals(properties.getValueType())) {
+                for (Map.Entry<String, JsonValue> stringJsonValueEntry : properties.asJsonObject().entrySet()) {
+                    final JsonValue value = stringJsonValueEntry.getValue();
+
+                    if (value instanceof JsonString) {
+                        tags.put(stringJsonValueEntry.getKey(), ((JsonString) value).getString());
+                    } else if (value instanceof JsonStructure) {
+                        Logging.warn(
+                            "The GeoJSON contains an object with property '" + stringJsonValueEntry.getKey()
+                                + "' whose value has the unsupported type '" + value.getClass().getSimpleName()
+                                + "'. That key-value pair is ignored!"
+                        );
+                    } else if (value.getValueType() != JsonValue.ValueType.NULL) {
+                        tags.put(stringJsonValueEntry.getKey(), value.toString());
+                    }
+                }
+            }
+        }
+        return tags;
+    }
+
+    @Override
+    protected DataSet doParseDataSet(InputStream source, ProgressMonitor progressMonitor) throws IllegalDataException {
+        setParser(Json.createParser(source));
+        parse();
+        return getDataSet();
+    }
+
+    /**
+     * Parse the given input source and return the dataset.
+     *
+     * @param source          the source input stream. Must not be null.
+     * @param progressMonitor the progress monitor. If null, {@link NullProgressMonitor#INSTANCE} is assumed
+     * @return the dataset with the parsed data
+     * @throws IllegalDataException     if an error was found while parsing the data from the source
+     * @throws IllegalArgumentException if source is null
+     */
+    public static DataSet parseDataSet(InputStream source, ProgressMonitor progressMonitor) throws IllegalDataException {
+        return new GeoJSONReader().doParseDataSet(source, progressMonitor);
+    }
+}
Index: /trunk/src/org/openstreetmap/josm/io/GeoJSONServerReader.java
===================================================================
--- /trunk/src/org/openstreetmap/josm/io/GeoJSONServerReader.java	(revision 15424)
+++ /trunk/src/org/openstreetmap/josm/io/GeoJSONServerReader.java	(revision 15424)
@@ -0,0 +1,40 @@
+// License: GPL. For details, see LICENSE file.
+package org.openstreetmap.josm.io;
+
+import static org.openstreetmap.josm.tools.I18n.tr;
+
+import java.util.Objects;
+
+import org.openstreetmap.josm.data.osm.DataSet;
+import org.openstreetmap.josm.gui.io.importexport.GeoJSONImporter;
+import org.openstreetmap.josm.gui.progress.ProgressMonitor;
+
+/**
+ * GeoJson server reader.
+ * @author Omar Vega Ramos &lt;ovruni@riseup.net&gt;
+ * @since 15424
+ */
+public class GeoJSONServerReader extends OsmServerReader {
+
+    private final String url;
+
+    /**
+     * Constructs a new {@code GeoJSONServerReader}.
+     * @param url geojson URL
+     */
+    public GeoJSONServerReader(String url) {
+        this.url = Objects.requireNonNull(url);
+    }
+
+    @Override
+    public DataSet parseOsm(ProgressMonitor progressMonitor) throws OsmTransferException {
+        try {
+            progressMonitor.beginTask(tr("Contacting Server…"), 10);
+            return new GeoJSONImporter().parseDataSet(url);
+        } catch (Exception e) {
+            throw new OsmTransferException(e);
+        } finally {
+            progressMonitor.finishTask();
+        }
+    }
+}
Index: /trunk/src/org/openstreetmap/josm/plugins/PluginHandler.java
===================================================================
--- /trunk/src/org/openstreetmap/josm/plugins/PluginHandler.java	(revision 15423)
+++ /trunk/src/org/openstreetmap/josm/plugins/PluginHandler.java	(revision 15424)
@@ -150,5 +150,6 @@
             new DeprecatedPlugin("download_along", inCore),
             new DeprecatedPlugin("plastic_laf", tr("no longer required")),
-            new DeprecatedPlugin("osmarender", tr("no longer required"))
+            new DeprecatedPlugin("osmarender", tr("no longer required")),
+            new DeprecatedPlugin("geojson", inCore)
         );
     }
Index: /trunk/test/data/geo.json
===================================================================
--- /trunk/test/data/geo.json	(revision 15424)
+++ /trunk/test/data/geo.json	(revision 15424)
@@ -0,0 +1,140 @@
+{
+    "type": "FeatureCollection",
+    "features": [
+        {
+            "type": "Feature",
+            "geometry": {
+                "type": "Point",
+                "coordinates": [
+                    102.0,
+                    0.5
+                ]
+            },
+            "properties": {
+                "propA": "valueA"
+            }
+        },
+        {
+            "type": "Feature",
+            "geometry": {
+                "type": "LineString",
+                "coordinates": [
+                    [
+                        102.0,
+                        0.0
+                    ],
+                    [
+                        103.0,
+                        1.0
+                    ],
+                    [
+                        104.0,
+                        0.0
+                    ],
+                    [
+                        105.0,
+                        1.0
+                    ]
+                ]
+            },
+            "properties": {
+                "propB": "valueB",
+                "propB2": 0.0
+            }
+        },
+        {
+            "type": "Feature",
+            "geometry": {
+                "type": "MultiPolygon",
+                "coordinates": [
+                    [
+                        [
+                            [
+                                180.0,
+                                40.0
+                            ],
+                            [
+                                180.0,
+                                50.0
+                            ],
+                            [
+                                170.0,
+                                50.0
+                            ],
+                            [
+                                170.0,
+                                40.0
+                            ],
+                            [
+                                180.0,
+                                40.0
+                            ]
+                        ]
+                    ],
+                    [
+                        [
+                            [
+                                -170.0,
+                                40.0
+                            ],
+                            [
+                                -170.0,
+                                50.0
+                            ],
+                            [
+                                -180.0,
+                                50.0
+                            ],
+                            [
+                                -180.0,
+                                40.0
+                            ],
+                            [
+                                -170.0,
+                                40.0
+                            ]
+                        ]
+                    ]
+                ]
+            }
+        },
+        {
+            "type": "Feature",
+            "geometry": {
+                "type": "Polygon",
+                "coordinates": [
+                    [
+                        [
+                            100.0,
+                            0.0
+                        ],
+                        [
+                            101.0,
+                            0.0
+                        ],
+                        [
+                            101.0,
+                            1.0
+                        ],
+                        [
+                            100.0,
+                            1.0
+                        ],
+                        [
+                            100.0,
+                            0.0
+                        ]
+                    ]
+                ]
+            },
+            "properties": {
+                "propD": "valueD",
+                "propD2": {
+                    "this": "that"
+                },
+                "propD3": true,
+                "propD4": null
+            }
+        }
+    ]
+}
Index: /trunk/test/unit/org/openstreetmap/josm/io/GeoJSONReaderTest.java
===================================================================
--- /trunk/test/unit/org/openstreetmap/josm/io/GeoJSONReaderTest.java	(revision 15424)
+++ /trunk/test/unit/org/openstreetmap/josm/io/GeoJSONReaderTest.java	(revision 15424)
@@ -0,0 +1,140 @@
+// License: GPL. For details, see LICENSE file.
+package org.openstreetmap.josm.io;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+
+import java.io.InputStream;
+import java.nio.file.Files;
+import java.nio.file.Paths;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Optional;
+
+import org.junit.Rule;
+import org.junit.Test;
+import org.openstreetmap.josm.TestUtils;
+import org.openstreetmap.josm.data.coor.LatLon;
+import org.openstreetmap.josm.data.osm.Node;
+import org.openstreetmap.josm.data.osm.OsmPrimitive;
+import org.openstreetmap.josm.data.osm.Way;
+import org.openstreetmap.josm.testutils.JOSMTestRules;
+
+/**
+ * Unit tests of {@link GeoJSONReader}.
+ */
+public class GeoJSONReaderTest {
+
+    /**
+     * Setup test.
+     */
+    @Rule
+    public JOSMTestRules rules = new JOSMTestRules();
+
+    /**
+     * Test reading a GeoJSON file.
+     * @throws Exception in case of error
+     */
+    @Test
+    public void test() throws Exception {
+        try (InputStream in = Files.newInputStream(Paths.get(TestUtils.getTestDataRoot(), "geo.json"))) {
+            final List<OsmPrimitive> primitives = new ArrayList<>(new GeoJSONReader()
+                .doParseDataSet(in, null)
+                .getPrimitives(it -> true));
+            assertEquals(21, primitives.size());
+
+            final Node node1 = new Node(new LatLon(0.5, 102.0));
+            final Optional<OsmPrimitive> foundNode1 = primitives.stream()
+                .filter(it -> areEqualNodes(it, node1))
+                .findFirst();
+            assertTrue(foundNode1.isPresent());
+            assertEquals("valueA", foundNode1.get().get("propA"));
+
+            final Way way1 = new Way();
+            way1.addNode(new Node(new LatLon(0, 102)));
+            way1.addNode(new Node(new LatLon(1, 103)));
+            way1.addNode(new Node(new LatLon(0, 104)));
+            way1.addNode(new Node(new LatLon(1, 105)));
+            final Optional<OsmPrimitive> foundWay1 = primitives.stream()
+                .filter(it -> areEqualWays(it, way1))
+                .findFirst();
+            assertTrue(foundWay1.isPresent());
+            assertEquals("valueB", foundWay1.get().get("propB"));
+            assertEquals("0.0", foundWay1.get().get("propB2"));
+
+            final Way way2 = new Way();
+            way2.addNode(new Node(new LatLon(40, 180)));
+            way2.addNode(new Node(new LatLon(50, 180)));
+            way2.addNode(new Node(new LatLon(50, 170)));
+            way2.addNode(new Node(new LatLon(40, 170)));
+            way2.addNode(new Node(new LatLon(40, 180)));
+            final Optional<OsmPrimitive> foundWay2 = primitives.stream()
+                .filter(it -> areEqualWays(it, way2))
+                .findFirst();
+            assertTrue(foundWay2.isPresent());
+            assertEquals(
+                ((Way) foundWay2.get()).getNode(0),
+                ((Way) foundWay2.get()).getNode(((Way) foundWay2.get()).getNodesCount() - 1)
+            );
+
+            final Way way3 = new Way();
+            way3.addNode(new Node(new LatLon(40, -170)));
+            way3.addNode(new Node(new LatLon(50, -170)));
+            way3.addNode(new Node(new LatLon(50, -180)));
+            way3.addNode(new Node(new LatLon(40, -180)));
+            way3.addNode(new Node(new LatLon(40, -170)));
+            final Optional<OsmPrimitive> foundWay3 = primitives.stream()
+                .filter(it -> areEqualWays(it, way3))
+                .findFirst();
+            assertTrue(foundWay3.isPresent());
+            assertEquals(
+                ((Way) foundWay3.get()).getNode(0),
+                ((Way) foundWay3.get()).getNode(((Way) foundWay3.get()).getNodesCount() - 1)
+            );
+
+            final Way way4 = new Way();
+            way4.addNode(new Node(new LatLon(0, 100)));
+            way4.addNode(new Node(new LatLon(0, 101)));
+            way4.addNode(new Node(new LatLon(1, 101)));
+            way4.addNode(new Node(new LatLon(1, 100)));
+            way4.addNode(new Node(new LatLon(0, 100)));
+            final Optional<OsmPrimitive> foundWay4 = primitives.stream()
+                .filter(it -> areEqualWays(it, way4))
+                .findFirst();
+            assertTrue(foundWay4.isPresent());
+            assertEquals(
+                ((Way) foundWay4.get()).getNode(0),
+                ((Way) foundWay4.get()).getNode(((Way) foundWay4.get()).getNodesCount() - 1)
+            );
+            assertEquals("valueD", foundWay4.get().get("propD"));
+            assertFalse(foundWay4.get().hasTag("propD2"));
+            assertEquals("true", foundWay4.get().get("propD3"));
+            assertFalse(foundWay4.get().hasKey("propD4"));
+            assertNull(foundWay4.get().get("propD4"));
+        }
+    }
+
+    private static boolean areEqualNodes(final OsmPrimitive p1, final OsmPrimitive p2) {
+        return (p1 instanceof Node)
+            && (p2 instanceof Node)
+            && ((Node) p1).getCoor().equals(((Node) p2).getCoor());
+    }
+
+    private static boolean areEqualWays(final OsmPrimitive p1, final OsmPrimitive p2) {
+        if (
+            (!(p1 instanceof Way))
+            || (!(p2 instanceof Way))
+            || ((Way) p1).getNodes().size() != ((Way) p2).getNodes().size()
+        ) {
+            return false;
+        }
+        for (int i = 0; i < ((Way) p1).getNodes().size(); i++) {
+            if (!areEqualNodes(((Way) p1).getNode(i), ((Way) p2).getNode(i))) {
+                return false;
+            }
+        }
+        return true;
+    }
+}
