Index: trunk/src/org/openstreetmap/josm/io/GeoJSONMapRouletteWriter.java
===================================================================
--- trunk/src/org/openstreetmap/josm/io/GeoJSONMapRouletteWriter.java	(revision 18753)
+++ trunk/src/org/openstreetmap/josm/io/GeoJSONMapRouletteWriter.java	(revision 18754)
@@ -6,5 +6,4 @@
 import java.util.stream.Stream;
 
-import jakarta.json.Json;
 import jakarta.json.JsonArray;
 import jakarta.json.JsonArrayBuilder;
@@ -41,7 +40,7 @@
      */
     public Optional<JsonObject> write(final TestError testError) {
-        final JsonObjectBuilder jsonObjectBuilder = Json.createObjectBuilder();
-        final JsonArrayBuilder featuresBuilder = Json.createArrayBuilder();
-        final JsonObjectBuilder propertiesBuilder = Json.createObjectBuilder();
+        final JsonObjectBuilder jsonObjectBuilder = JSON_PROVIDER.createObjectBuilder();
+        final JsonArrayBuilder featuresBuilder = JSON_PROVIDER.createArrayBuilder();
+        final JsonObjectBuilder propertiesBuilder = JSON_PROVIDER.createObjectBuilder();
         propertiesBuilder.add("message", testError.getMessage());
         Optional.ofNullable(testError.getDescription()).ifPresent(description -> propertiesBuilder.add("description", description));
@@ -62,5 +61,5 @@
                 .forEach(primitive -> super.appendPrimitive(primitive, featuresBuilder));
         final JsonArray featureArray = featuresBuilder.build();
-        final JsonArrayBuilder featuresMessageBuilder = Json.createArrayBuilder();
+        final JsonArrayBuilder featuresMessageBuilder = JSON_PROVIDER.createArrayBuilder();
         if (featureArray.isEmpty()) {
             Logging.trace("Could not generate task for {0}", testError.getMessage());
@@ -68,10 +67,10 @@
         }
         JsonObject primitive = featureArray.getJsonObject(0);
-        JsonObjectBuilder replacementPrimitive = Json.createObjectBuilder(primitive);
+        JsonObjectBuilder replacementPrimitive = JSON_PROVIDER.createObjectBuilder(primitive);
         final JsonObjectBuilder properties;
         if (primitive.containsKey("properties") && primitive.get("properties").getValueType() == JsonValue.ValueType.OBJECT) {
-            properties = Json.createObjectBuilder(primitive.getJsonObject("properties"));
+            properties = JSON_PROVIDER.createObjectBuilder(primitive.getJsonObject("properties"));
         } else {
-            properties = Json.createObjectBuilder();
+            properties = JSON_PROVIDER.createObjectBuilder();
         }
         properties.addAll(propertiesBuilder);
Index: trunk/src/org/openstreetmap/josm/io/GeoJSONWriter.java
===================================================================
--- trunk/src/org/openstreetmap/josm/io/GeoJSONWriter.java	(revision 18753)
+++ trunk/src/org/openstreetmap/josm/io/GeoJSONWriter.java	(revision 18754)
@@ -27,4 +27,5 @@
 import jakarta.json.JsonValue;
 import jakarta.json.JsonWriter;
+import jakarta.json.spi.JsonProvider;
 import jakarta.json.stream.JsonGenerator;
 import jakarta.json.stream.JsonParser;
@@ -74,6 +75,16 @@
     private static final BooleanProperty SKIP_EMPTY_NODES = new BooleanProperty("geojson.export.skip-empty-nodes", true);
     private static final BooleanProperty UNTAGGED_CLOSED_IS_POLYGON = new BooleanProperty("geojson.export.untagged-closed-is-polygon", false);
+
+    /**
+     * Used to avoid many calls to {@link JsonProvider#provider} in {@link #getCoorArray(JsonArrayBuilder, EastNorth)}.
+     * For validating Mesa County, CO, this reduces CPU and memory usage of {@link #write()} by ~80%. By using this for
+     * other {@link Json} calls, {@link #write()} takes ~95% less resources than the original. And the entire process
+     * takes 1/4 of the time (38 minutes -&gt; <10 minutes).
+     * <p>
+     * For more details, see <a href="https://github.com/jakartaee/jsonp-api/issues/346">JSONP #346</a>.
+     */
+    protected static final JsonProvider JSON_PROVIDER = JsonProvider.provider();
     private static final Set<Way> processedMultipolygonWays = new HashSet<>();
-    private EnumSet<Options> options = EnumSet.noneOf(Options.class);
+    private final EnumSet<Options> options = EnumSet.noneOf(Options.class);
 
     /**
@@ -138,6 +149,6 @@
     public void write(boolean pretty, Writer writer) {
         Map<String, Object> config = Collections.singletonMap(JsonGenerator.PRETTY_PRINTING, pretty);
-        try (JsonWriter jsonWriter = Json.createWriterFactory(config).createWriter(writer)) {
-            JsonObjectBuilder object = Json.createObjectBuilder()
+        try (JsonWriter jsonWriter = JSON_PROVIDER.createWriterFactory(config).createWriter(writer)) {
+            JsonObjectBuilder object = JSON_PROVIDER.createObjectBuilder()
                     .add("type", "FeatureCollection")
                     .add("generator", "JOSM");
@@ -184,5 +195,5 @@
                 if (writeAsPolygon) {
                     geomObj.add("type", "Polygon");
-                    geomObj.add("coordinates", Json.createArrayBuilder().add(array));
+                    geomObj.add("coordinates", JSON_PROVIDER.createArrayBuilder().add(array));
                 } else {
                     geomObj.add("type", "LineString");
@@ -223,8 +234,8 @@
          */
         private void visitMultiGeometry(final Relation r) {
-            final JsonArrayBuilder jsonArrayBuilder = Json.createArrayBuilder();
+            final JsonArrayBuilder jsonArrayBuilder = JSON_PROVIDER.createArrayBuilder();
             r.getMemberPrimitives().stream().filter(p -> !(p instanceof Relation))
                     .map(p -> {
-                        final JsonObjectBuilder tempGeomObj = Json.createObjectBuilder();
+                        final JsonObjectBuilder tempGeomObj = JSON_PROVIDER.createObjectBuilder();
                         p.accept(new GeometryPrimitiveVisitor(tempGeomObj));
                         return tempGeomObj.build();
@@ -239,5 +250,5 @@
          */
         private void visitMultiPoints(final Relation r) {
-            final JsonArrayBuilder multiPoint = Json.createArrayBuilder();
+            final JsonArrayBuilder multiPoint = JSON_PROVIDER.createArrayBuilder();
             r.getMembers().stream().map(RelationMember::getMember).filter(Node.class::isInstance).map(Node.class::cast)
                     .map(latLon -> getCoorArray(null, latLon))
@@ -252,5 +263,5 @@
          */
         private void visitMultiLineString(final Relation r) {
-            final JsonArrayBuilder multiLine = Json.createArrayBuilder();
+            final JsonArrayBuilder multiLine = JSON_PROVIDER.createArrayBuilder();
             r.getMembers().stream().map(RelationMember::getMember).filter(Way.class::isInstance).map(Way.class::cast)
                     .map(Way::getNodes).map(p -> {
@@ -274,5 +285,5 @@
                 final Pair<List<MultipolygonBuilder.JoinedPolygon>, List<MultipolygonBuilder.JoinedPolygon>> mp =
                         MultipolygonBuilder.joinWays(r);
-                final JsonArrayBuilder polygon = Json.createArrayBuilder();
+                final JsonArrayBuilder polygon = JSON_PROVIDER.createArrayBuilder();
                 // Peek would theoretically be better for these two streams, but SonarLint doesn't like it.
                 // java:S3864: "Stream.peek" should be used with caution
@@ -303,5 +314,5 @@
                         })
                         .forEach(polygon::add);
-                final JsonArrayBuilder multiPolygon = Json.createArrayBuilder().add(polygon);
+                final JsonArrayBuilder multiPolygon = JSON_PROVIDER.createArrayBuilder().add(polygon);
                 geomObj.add("type", "MultiPolygon");
                 geomObj.add("coordinates", multiPolygon);
@@ -310,5 +321,5 @@
 
         private JsonArrayBuilder getCoorsArray(Iterable<Node> nodes) {
-            final JsonArrayBuilder builder = Json.createArrayBuilder();
+            final JsonArrayBuilder builder = JSON_PROVIDER.createArrayBuilder();
             for (Node n : nodes) {
                 if (n.isLatLonKnown()) {
@@ -325,5 +336,5 @@
 
     private static JsonArrayBuilder getCoorArray(JsonArrayBuilder builder, EastNorth c) {
-        return (builder != null ? builder : Json.createArrayBuilder())
+        return (builder != null ? builder : JSON_PROVIDER.createArrayBuilder())
                 .add(BigDecimal.valueOf(c.getX()).setScale(11, RoundingMode.HALF_UP))
                 .add(BigDecimal.valueOf(c.getY()).setScale(11, RoundingMode.HALF_UP));
@@ -337,5 +348,5 @@
 
         // Properties
-        final JsonObjectBuilder propObj = Json.createObjectBuilder();
+        final JsonObjectBuilder propObj = JSON_PROVIDER.createObjectBuilder();
         for (Map.Entry<String, String> t : p.getKeys().entrySet()) {
             // If writing OSM information, follow Overpass syntax (escape `@` with another `@`)
@@ -357,7 +368,7 @@
             }
             if (options.contains(Options.WRITE_OSM_INFORMATION) && p.getReferrers(true).stream().anyMatch(Relation.class::isInstance)) {
-                final JsonArrayBuilder jsonArrayBuilder = Json.createArrayBuilder();
+                final JsonArrayBuilder jsonArrayBuilder = JSON_PROVIDER.createArrayBuilder();
                 for (Relation relation : Utils.filteredCollection(p.getReferrers(), Relation.class)) {
-                    final JsonObjectBuilder relationObject = Json.createObjectBuilder();
+                    final JsonObjectBuilder relationObject = JSON_PROVIDER.createObjectBuilder();
                     relationObject.add("rel", relation.getId());
                     Collection<RelationMember> members = relation.getMembersFor(Collections.singleton(p));
@@ -365,5 +376,5 @@
                     relationObject.add("role",
                             members.stream().map(RelationMember::getRole).collect(Collectors.joining(";")));
-                    final JsonObjectBuilder relationKeys = Json.createObjectBuilder();
+                    final JsonObjectBuilder relationKeys = JSON_PROVIDER.createObjectBuilder();
                     // Uncertain if the @relation reltags need to be @ escaped. I don't think so, as example output
                     // didn't have any metadata in it.
@@ -379,5 +390,5 @@
 
         // Geometry
-        final JsonObjectBuilder geomObj = Json.createObjectBuilder();
+        final JsonObjectBuilder geomObj = JSON_PROVIDER.createObjectBuilder();
         p.accept(new GeometryPrimitiveVisitor(geomObj));
         final JsonObject geom = geomObj.build();
@@ -385,5 +396,5 @@
         if (!geom.isEmpty()) {
             // Build primitive JSON object
-            array.add(Json.createObjectBuilder()
+            array.add(JSON_PROVIDER.createObjectBuilder()
                     .add("type", "Feature")
                     .add("properties", prop.isEmpty() ? JsonValue.NULL : prop)
@@ -394,5 +405,5 @@
     private static JsonValue convertValueToJson(String value) {
         if (value.startsWith(JSON_VALUE_START_MARKER) && value.endsWith(JSON_VALUE_END_MARKER)) {
-            try (JsonParser parser = Json.createParser(new StringReader(value))) {
+            try (JsonParser parser = JSON_PROVIDER.createParser(new StringReader(value))) {
                 if (parser.hasNext() && parser.next() != null) {
                     return parser.getValue();
@@ -402,5 +413,5 @@
             }
         }
-        return Json.createValue(value);
+        return JSON_PROVIDER.createValue(value);
     }
 
@@ -420,5 +431,5 @@
     protected void appendBounds(Bounds b, JsonObjectBuilder object) {
         if (b != null) {
-            JsonArrayBuilder builder = Json.createArrayBuilder();
+            JsonArrayBuilder builder = JSON_PROVIDER.createArrayBuilder();
             getCoorArray(builder, b.getMin());
             getCoorArray(builder, b.getMax());
@@ -428,5 +439,5 @@
 
     protected void appendLayerFeatures(DataSet ds, JsonObjectBuilder object) {
-        JsonArrayBuilder array = Json.createArrayBuilder();
+        JsonArrayBuilder array = JSON_PROVIDER.createArrayBuilder();
         if (ds != null) {
             processedMultipolygonWays.clear();
