This is an automated email from the ASF dual-hosted git repository.

asf-gitbox-commits pushed a commit to branch geoapi-4.0
in repository https://gitbox.apache.org/repos/asf/sis.git


The following commit(s) were added to refs/heads/geoapi-4.0 by this push:
     new c43101db20 migrate(coveragejson): replace jsonb bindings by jackson
c43101db20 is described below

commit c43101db2038bc06f5d3728ae487204ed75555e0
Author: jsorel <[email protected]>
AuthorDate: Tue Jul 28 11:51:21 2026 +0200

    migrate(coveragejson): replace jsonb bindings by jackson
---
 incubator/build.gradle.kts                         |  13 +--
 .../main/module-info.java                          |  17 +---
 .../storage/coveragejson/CoverageJsonStore.java    |  17 ++--
 .../sis/storage/coveragejson/CoverageResource.java |  18 ++--
 .../sis/storage/coveragejson/binding/Axe.java      |   9 +-
 .../sis/storage/coveragejson/binding/Axes.java     |  16 ++--
 .../sis/storage/coveragejson/binding/Category.java |  10 +--
 .../coveragejson/binding/CategoryEncoding.java     |  51 +----------
 .../sis/storage/coveragejson/binding/Coverage.java |  47 +++++-----
 .../coveragejson/binding/CoverageCollection.java   |  10 +--
 .../coveragejson/binding/CoverageJsonObject.java   |  37 ++++----
 .../storage/coveragejson/binding/Dictionary.java   |  73 ---------------
 .../sis/storage/coveragejson/binding/Domain.java   |   7 +-
 .../coveragejson/binding/GeographicCRS.java        |   7 +-
 .../sis/storage/coveragejson/binding/I18N.java     |  68 +++-----------
 .../storage/coveragejson/binding/IdentifierRS.java |  10 +--
 .../storage/coveragejson/binding/Identifiers.java  |  31 -------
 .../sis/storage/coveragejson/binding/NdArray.java  |   7 +-
 .../coveragejson/binding/ObservedProperty.java     |   9 +-
 .../storage/coveragejson/binding/Parameter.java    |   7 +-
 .../coveragejson/binding/ParameterGroup.java       |   7 +-
 .../storage/coveragejson/binding/Parameters.java   |  78 ----------------
 .../storage/coveragejson/binding/ProjectedCRS.java |   7 +-
 .../sis/storage/coveragejson/binding/Ranges.java   |  74 ---------------
 .../binding/ReferenceSystemConnection.java         |   9 +-
 .../sis/storage/coveragejson/binding/Symbol.java   |   9 +-
 .../coveragejson/binding/TargetConcept.java        |   9 +-
 .../storage/coveragejson/binding/TemporalRS.java   |   7 +-
 .../sis/storage/coveragejson/binding/TileSet.java  |   9 +-
 .../storage/coveragejson/binding/TiledNdArray.java |   9 +-
 .../sis/storage/coveragejson/binding/Unit.java     |   9 +-
 .../storage/coveragejson/binding/VerticalCRS.java  |   7 +-
 .../sis/storage/json/DataTransferObject.java       | 100 +++++++++++++++++++++
 .../storage/coveragejson/binding/BindingTest.java  |  38 ++++----
 netbeans-project/ivy.xml                           |   5 ++
 35 files changed, 287 insertions(+), 554 deletions(-)

diff --git a/incubator/build.gradle.kts b/incubator/build.gradle.kts
index 063cb72947..115dc258f4 100644
--- a/incubator/build.gradle.kts
+++ b/incubator/build.gradle.kts
@@ -57,11 +57,14 @@ dependencies {
      * Dependencies used only by incubated modules. The dependencies are not 
declared
      * in the global `settings.gradle.kts` because incubated modules are not 
released.
      */
-    implementation(group = "org.antlr",       name = "antlr4-maven-plugin", 
version = "4.13.2")
-    implementation(group = "org.xerial",      name = "sqlite-jdbc",         
version = "3.45.1.0")
-    compileOnly   (group = "jakarta.servlet", name = "jakarta.servlet-api", 
version = "6.1.0")
-    compileOnly   (group = "org.osgi",        name = "osgi.core",           
version = "8.0.0")
-    antlr         (group = "org.antlr",       name = "antlr4",              
version = "4.13.2")
+    implementation(group = "com.fasterxml.jackson.core", name = 
"jackson-core",        version = "2.22.1")
+    implementation(group = "com.fasterxml.jackson.core", name = 
"jackson-annotations", version = "2.22")
+    implementation(group = "com.fasterxml.jackson.core", name = 
"jackson-databind",    version = "2.22.1")
+    implementation(group = "org.antlr",                  name = 
"antlr4-maven-plugin", version = "4.13.2")
+    implementation(group = "org.xerial",                 name = "sqlite-jdbc", 
        version = "3.45.1.0")
+    compileOnly   (group = "jakarta.servlet",            name = 
"jakarta.servlet-api", version = "6.1.0")
+    compileOnly   (group = "org.osgi",                   name = "osgi.core",   
        version = "8.0.0")
+    antlr         (group = "org.antlr",                  name = "antlr4",      
        version = "4.13.2")
 }
 
 /*
diff --git a/incubator/src/org.apache.sis.storage.json/main/module-info.java 
b/incubator/src/org.apache.sis.storage.json/main/module-info.java
index 77b024eeb9..e4c01c7933 100644
--- a/incubator/src/org.apache.sis.storage.json/main/module-info.java
+++ b/incubator/src/org.apache.sis.storage.json/main/module-info.java
@@ -31,23 +31,14 @@ module org.apache.sis.storage.json {
     requires transitive org.apache.sis.storage;
 
     // Dependencies internal to the implementation.
-    requires jakarta.json;
-    requires jakarta.json.bind;
-
-    // Temporary dependency used by BindingTest class. TODO: remove that 
dependency.
-    requires org.eclipse.yasson;
+    requires com.fasterxml.jackson.core;
+    requires com.fasterxml.jackson.annotation;
+    requires com.fasterxml.jackson.databind;
 
     provides org.apache.sis.storage.DataStoreProvider
         with org.apache.sis.storage.coveragejson.CoverageJsonStoreProvider;
 
+    exports org.apache.sis.storage.json;
     exports org.apache.sis.storage.coveragejson;
-
-    /*
-     * TODO
-     * Following should be a qualified export to `org.eclipse.yasson` only.
-     * However it does not work with Yasson:
-     *
-     * https://github.com/eclipse-ee4j/yasson/issues/545
-     */
     exports org.apache.sis.storage.coveragejson.binding;
 }
diff --git 
a/incubator/src/org.apache.sis.storage.json/main/org/apache/sis/storage/coveragejson/CoverageJsonStore.java
 
b/incubator/src/org.apache.sis.storage.json/main/org/apache/sis/storage/coveragejson/CoverageJsonStore.java
index b66562a3d0..78d832d7d9 100644
--- 
a/incubator/src/org.apache.sis.storage.json/main/org/apache/sis/storage/coveragejson/CoverageJsonStore.java
+++ 
b/incubator/src/org.apache.sis.storage.json/main/org/apache/sis/storage/coveragejson/CoverageJsonStore.java
@@ -16,6 +16,7 @@
  */
 package org.apache.sis.storage.coveragejson;
 
+import com.fasterxml.jackson.databind.ObjectMapper;
 import java.io.BufferedInputStream;
 import java.io.IOException;
 import java.io.InputStream;
@@ -28,9 +29,6 @@ import java.util.Collection;
 import java.util.Collections;
 import java.util.List;
 import java.util.Optional;
-import jakarta.json.bind.Jsonb;
-import jakarta.json.bind.JsonbBuilder;
-import jakarta.json.bind.JsonbConfig;
 import org.opengis.metadata.Metadata;
 import org.opengis.parameter.ParameterValueGroup;
 import org.apache.sis.coverage.grid.GridCoverage;
@@ -96,9 +94,8 @@ public class CoverageJsonStore extends DataStore implements 
WritableAggregate {
         if (!parsed) {
             parsed = true;
             if (Files.exists(path)) {
-                try (Jsonb b = JsonbBuilder.create();
-                     InputStream in = new 
BufferedInputStream(Files.newInputStream(path))) {
-                    final CoverageJsonObject obj = b.fromJson(in, 
CoverageJsonObject.class);
+                try (InputStream in = new 
BufferedInputStream(Files.newInputStream(path))) {
+                    final CoverageJsonObject obj = new 
ObjectMapper().readValue(in, CoverageJsonObject.class);
 
                     if (obj instanceof Coverage) {
                         final Coverage coverage = (Coverage) obj;
@@ -159,8 +156,8 @@ public class CoverageJsonStore extends DataStore implements 
WritableAggregate {
             //single coverage
             final CoverageResource res = (CoverageResource) components.get(0);
 
-            try (Jsonb jsonb = JsonbBuilder.create(new 
JsonbConfig().withFormatting(true))) {
-                json = jsonb.toJson(res.getBinding());
+            try {
+                json = new ObjectMapper().writeValueAsString(res.getBinding());
             } catch (Exception ex) {
                 throw new DataStoreException("Failed to create coverage json 
binding", ex);
             }
@@ -172,8 +169,8 @@ public class CoverageJsonStore extends DataStore implements 
WritableAggregate {
                 col.coverages.add(res.getBinding());
             }
 
-            try (Jsonb jsonb = JsonbBuilder.create(new 
JsonbConfig().withFormatting(true))) {
-                json = jsonb.toJson(col);
+            try {
+                json = new ObjectMapper().writeValueAsString(col);
             } catch (Exception ex) {
                 throw new DataStoreException("Failed to create coverage 
collection json binding", ex);
             }
diff --git 
a/incubator/src/org.apache.sis.storage.json/main/org/apache/sis/storage/coveragejson/CoverageResource.java
 
b/incubator/src/org.apache.sis.storage.json/main/org/apache/sis/storage/coveragejson/CoverageResource.java
index c7fa60c943..e24323c1e2 100644
--- 
a/incubator/src/org.apache.sis.storage.json/main/org/apache/sis/storage/coveragejson/CoverageResource.java
+++ 
b/incubator/src/org.apache.sis.storage.json/main/org/apache/sis/storage/coveragejson/CoverageResource.java
@@ -66,9 +66,7 @@ import 
org.apache.sis.storage.coveragejson.binding.IdentifierRS;
 import org.apache.sis.storage.coveragejson.binding.NdArray;
 import org.apache.sis.storage.coveragejson.binding.ObservedProperty;
 import org.apache.sis.storage.coveragejson.binding.Parameter;
-import org.apache.sis.storage.coveragejson.binding.Parameters;
 import org.apache.sis.storage.coveragejson.binding.ProjectedCRS;
-import org.apache.sis.storage.coveragejson.binding.Ranges;
 import org.apache.sis.storage.coveragejson.binding.ReferenceSystemConnection;
 import org.apache.sis.storage.coveragejson.binding.TemporalRS;
 import org.apache.sis.storage.coveragejson.binding.VerticalCRS;
@@ -127,13 +125,13 @@ final class CoverageResource extends 
AbstractGridCoverageResource {
 
         // Rebuild grid geometry
         try {
-            gridGeometry = jsonToGridGeometry(binding.domain);
+            gridGeometry = jsonToGridGeometry((Domain)binding.domain);
         } catch (FactoryException ex) {
             throw new DataStoreException("Failed to create GridGeometry from 
JSON Domain", ex);
         }
         // Rebuild sample dimensions
         sampleDimensions = new ArrayList<>();
-        for (Entry<String,Parameter> entry : 
binding.parameters.any.entrySet()) {
+        for (Entry<String,Parameter> entry : binding.parameters.entrySet()) {
             final SampleDimension sd = jsonToSampleDimension(entry.getKey(), 
entry.getValue());
             sampleDimensions.add(sd);
         }
@@ -142,7 +140,7 @@ final class CoverageResource extends 
AbstractGridCoverageResource {
         }
         // Read datas
         datas = new HashMap<>();
-        for (Entry<String,NdArray> entry : binding.ranges.any.entrySet()) {
+        for (Entry<String,NdArray> entry : binding.ranges.entrySet()) {
             datas.put(entry.getKey(), jsonToDataBuffer(entry.getValue()));
         }
     }
@@ -488,15 +486,15 @@ final class CoverageResource extends 
AbstractGridCoverageResource {
         }
 
         // Build parameters
-        binding.parameters = new Parameters();
+        binding.parameters = new LinkedHashMap<>();
         for (SampleDimension sd : coverage.getSampleDimensions()) {
             final Entry<String, Parameter> entry = sampleDimensionToJson(sd);
-            binding.parameters.setAnyProperty(entry.getKey(), 
entry.getValue());
+            binding.parameters.put(entry.getKey(), entry.getValue());
         }
 
         // Build datas
-        binding.ranges = new Ranges();
-        binding.ranges.any.putAll(imageToJson(coverage, new 
ArrayList<>(binding.parameters.any.keySet())));
+        binding.ranges = new LinkedHashMap<>();
+        binding.ranges.putAll(imageToJson(coverage, new 
ArrayList<>(binding.parameters.keySet())));
 
         return binding;
     }
@@ -635,7 +633,7 @@ final class CoverageResource extends 
AbstractGridCoverageResource {
                 for (double i = min; i < max; i++) {
                     values.add((int) i);
                 }
-                catEnc.any.put(catb.id, values);
+                catEnc.setOtherField(catb.id, values);
             }
         }
 
diff --git 
a/incubator/src/org.apache.sis.storage.json/main/org/apache/sis/storage/coveragejson/binding/Axe.java
 
b/incubator/src/org.apache.sis.storage.json/main/org/apache/sis/storage/coveragejson/binding/Axe.java
index 5df56371ef..f510948ce0 100644
--- 
a/incubator/src/org.apache.sis.storage.json/main/org/apache/sis/storage/coveragejson/binding/Axe.java
+++ 
b/incubator/src/org.apache.sis.storage.json/main/org/apache/sis/storage/coveragejson/binding/Axe.java
@@ -16,10 +16,10 @@
  */
 package org.apache.sis.storage.coveragejson.binding;
 
+import com.fasterxml.jackson.annotation.JsonPropertyOrder;
 import java.util.List;
 import java.util.Objects;
-import jakarta.json.bind.annotation.JsonbNillable;
-import jakarta.json.bind.annotation.JsonbPropertyOrder;
+import org.apache.sis.storage.json.DataTransferObject;
 
 
 /**
@@ -30,9 +30,8 @@ import jakarta.json.bind.annotation.JsonbPropertyOrder;
  *
  * @author Johann Sorel (Geomatys)
  */
-@JsonbNillable(false)
-@JsonbPropertyOrder({"start","stop","num","dataType","coordinates","values","bounds"})
-public final class Axe extends Dictionary<Object> {
+@JsonPropertyOrder({"start","stop","num","dataType","coordinates","values","bounds"})
+public final class Axe extends DataTransferObject {
 
     public static final String DATATYPE_PRIMITIVE = "primitive";
     public static final String DATATYPE_TUPLE = "tuple";
diff --git 
a/incubator/src/org.apache.sis.storage.json/main/org/apache/sis/storage/coveragejson/binding/Axes.java
 
b/incubator/src/org.apache.sis.storage.json/main/org/apache/sis/storage/coveragejson/binding/Axes.java
index 0ec47cb7db..a72d1902a1 100644
--- 
a/incubator/src/org.apache.sis.storage.json/main/org/apache/sis/storage/coveragejson/binding/Axes.java
+++ 
b/incubator/src/org.apache.sis.storage.json/main/org/apache/sis/storage/coveragejson/binding/Axes.java
@@ -16,9 +16,9 @@
  */
 package org.apache.sis.storage.coveragejson.binding;
 
+import com.fasterxml.jackson.annotation.JsonPropertyOrder;
 import java.util.Objects;
-import jakarta.json.bind.annotation.JsonbNillable;
-import jakarta.json.bind.annotation.JsonbPropertyOrder;
+import org.apache.sis.storage.json.DataTransferObject;
 
 
 /**
@@ -27,14 +27,14 @@ import jakarta.json.bind.annotation.JsonbPropertyOrder;
  *
  * @author Johann Sorel (Geomatys)
  */
-@JsonbNillable(false)
-@JsonbPropertyOrder({"x","y","z","t"})
-public final class Axes extends Dictionary<Object> {
+@JsonPropertyOrder({"x","y","z","t","composite"})
+public final class Axes extends DataTransferObject {
 
     public Axe x;
     public Axe y;
     public Axe z;
     public Axe t;
+    public Axe composite;
 
     public Axes() {
     }
@@ -49,7 +49,8 @@ public final class Axes extends Dictionary<Object> {
             && Objects.equals(x, cdt.x)
             && Objects.equals(y, cdt.y)
             && Objects.equals(y, cdt.y)
-            && Objects.equals(t, cdt.t);
+            && Objects.equals(t, cdt.t)
+            && Objects.equals(composite, cdt.composite);
     }
 
     @Override
@@ -58,6 +59,7 @@ public final class Axes extends Dictionary<Object> {
                 x,
                 y,
                 z,
-                t);
+                t,
+                composite);
     }
 }
diff --git 
a/incubator/src/org.apache.sis.storage.json/main/org/apache/sis/storage/coveragejson/binding/Category.java
 
b/incubator/src/org.apache.sis.storage.json/main/org/apache/sis/storage/coveragejson/binding/Category.java
index c75c805bfc..0f465398c1 100644
--- 
a/incubator/src/org.apache.sis.storage.json/main/org/apache/sis/storage/coveragejson/binding/Category.java
+++ 
b/incubator/src/org.apache.sis.storage.json/main/org/apache/sis/storage/coveragejson/binding/Category.java
@@ -16,10 +16,9 @@
  */
 package org.apache.sis.storage.coveragejson.binding;
 
+import com.fasterxml.jackson.annotation.JsonPropertyOrder;
 import java.util.Objects;
-import jakarta.json.bind.annotation.JsonbNillable;
-import jakarta.json.bind.annotation.JsonbPropertyOrder;
-
+import org.apache.sis.storage.json.DataTransferObject;
 
 /**
  * COPIED FROM OGC SPECIFICATION (TODO: ADAPT):
@@ -28,9 +27,8 @@ import jakarta.json.bind.annotation.JsonbPropertyOrder;
  *
  * @author Johann Sorel (Geomatys)
  */
-@JsonbNillable(false)
-@JsonbPropertyOrder({"id","label","description"})
-public final class Category extends Dictionary<Object> {
+@JsonPropertyOrder({"id","label","description"})
+public final class Category extends DataTransferObject {
     /**
      * COPIED FROM OGC SPECIFICATION (TODO: ADAPT):
      * The value of "id" MUST be a string and SHOULD be a common identifier.
diff --git 
a/incubator/src/org.apache.sis.storage.json/main/org/apache/sis/storage/coveragejson/binding/CategoryEncoding.java
 
b/incubator/src/org.apache.sis.storage.json/main/org/apache/sis/storage/coveragejson/binding/CategoryEncoding.java
index 09c7f49653..4f0dde401c 100644
--- 
a/incubator/src/org.apache.sis.storage.json/main/org/apache/sis/storage/coveragejson/binding/CategoryEncoding.java
+++ 
b/incubator/src/org.apache.sis.storage.json/main/org/apache/sis/storage/coveragejson/binding/CategoryEncoding.java
@@ -16,18 +16,7 @@
  */
 package org.apache.sis.storage.coveragejson.binding;
 
-import java.lang.reflect.Type;
-import java.util.Map;
-import jakarta.json.bind.annotation.JsonbTypeDeserializer;
-import jakarta.json.bind.annotation.JsonbTypeSerializer;
-import jakarta.json.bind.serializer.DeserializationContext;
-import jakarta.json.bind.serializer.JsonbDeserializer;
-import jakarta.json.bind.serializer.JsonbSerializer;
-import jakarta.json.bind.serializer.SerializationContext;
-import jakarta.json.stream.JsonGenerator;
-import jakarta.json.stream.JsonParser;
-import 
org.apache.sis.storage.coveragejson.binding.CategoryEncoding.Deserializer;
-import org.apache.sis.storage.coveragejson.binding.CategoryEncoding.Serializer;
+import org.apache.sis.storage.json.DataTransferObject;
 
 
 /**
@@ -40,43 +29,9 @@ import 
org.apache.sis.storage.coveragejson.binding.CategoryEncoding.Serializer;
  *
  * @author Johann Sorel (Geomatys)
  */
-@JsonbTypeDeserializer(Deserializer.class)
-@JsonbTypeSerializer(Serializer.class)
-public final class CategoryEncoding extends Dictionary<Object> {
-    public CategoryEncoding() {
-    }
-
-    public static class Deserializer implements 
JsonbDeserializer<CategoryEncoding> {
-        public Deserializer() {
-        }
+public final class CategoryEncoding extends DataTransferObject {
 
-        @Override
-        public CategoryEncoding deserialize(JsonParser parser, 
DeserializationContext ctx, Type rtType) {
-            final CategoryEncoding candidate = new CategoryEncoding();
-            while (parser.hasNext()) {
-                final JsonParser.Event event = parser.next();
-                if (event == JsonParser.Event.KEY_NAME) {
-                    // Deserialize inner object
-                    final String name = parser.getString();
-                    String value = ctx.deserialize(String.class, parser);
-                    candidate.setAnyProperty(name, value);
-                }
-            }
-            return candidate;
-        }
+    public CategoryEncoding() {
     }
 
-    public static class Serializer implements 
JsonbSerializer<CategoryEncoding> {
-        public Serializer() {
-        }
-
-        @Override
-        public void serialize(CategoryEncoding ranges, JsonGenerator jg, 
SerializationContext sc) {
-            jg.writeStartObject();
-            for (Map.Entry<String,Object> entry : ranges.any.entrySet()) {
-                sc.serialize(entry.getKey(), entry.getValue(), jg);
-            }
-            jg.writeEnd();
-        }
-    }
 }
diff --git 
a/incubator/src/org.apache.sis.storage.json/main/org/apache/sis/storage/coveragejson/binding/Coverage.java
 
b/incubator/src/org.apache.sis.storage.json/main/org/apache/sis/storage/coveragejson/binding/Coverage.java
index 435ee435cf..21d7f76111 100644
--- 
a/incubator/src/org.apache.sis.storage.json/main/org/apache/sis/storage/coveragejson/binding/Coverage.java
+++ 
b/incubator/src/org.apache.sis.storage.json/main/org/apache/sis/storage/coveragejson/binding/Coverage.java
@@ -16,15 +16,17 @@
  */
 package org.apache.sis.storage.coveragejson.binding;
 
-import java.lang.reflect.Type;
+import com.fasterxml.jackson.annotation.JsonPropertyOrder;
+import com.fasterxml.jackson.core.JacksonException;
+import com.fasterxml.jackson.core.JsonParser;
+import com.fasterxml.jackson.databind.DeserializationContext;
+import com.fasterxml.jackson.databind.JsonDeserializer;
+import com.fasterxml.jackson.databind.JsonNode;
+import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
+import java.io.IOException;
 import java.util.List;
+import java.util.Map;
 import java.util.Objects;
-import jakarta.json.bind.JsonbException;
-import jakarta.json.bind.annotation.JsonbNillable;
-import jakarta.json.bind.annotation.JsonbPropertyOrder;
-import jakarta.json.bind.serializer.DeserializationContext;
-import jakarta.json.bind.serializer.JsonbDeserializer;
-import jakarta.json.stream.JsonParser;
 
 
 /**
@@ -33,8 +35,7 @@ import jakarta.json.stream.JsonParser;
  *
  * @author Johann Sorel (Geomatys)
  */
-@JsonbNillable(false)
-@JsonbPropertyOrder({"type","id","domain","parameters","parameterGroups","ranges"})
+@JsonPropertyOrder({"type","id","domain","parameters","parameterGroups","ranges"})
 public final class Coverage extends CoverageJsonObject {
     /**
      * COPIED FROM OGC SPECIFICATION (TODO: ADAPT):
@@ -56,9 +57,8 @@ public final class Coverage extends CoverageJsonObject {
      * "domainType" member then that member SHOULD be omitted in the coverage
      * object.
      */
-    //@JsonbTypeDeserializer(Coverage.DomainDeserializer.class)
-    //TODO should be a Domain or an URL, DomainDeserializer not working as 
expected
-    public Domain domain;
+    @JsonDeserialize(using = DomainDeserializer.class)
+    public Object domain;
 
     /**
      * COPIED FROM OGC SPECIFICATION (TODO: ADAPT):
@@ -72,7 +72,7 @@ public final class Coverage extends CoverageJsonObject {
      * is not part of a coverage collection or if the coverage collection does
      * not have a "parameters" member.
      */
-    public Parameters parameters;
+    public Map<String,Parameter> parameters;
 
     /**
      * COPIED FROM OGC SPECIFICATION (TODO: ADAPT):
@@ -98,9 +98,10 @@ public final class Coverage extends CoverageJsonObject {
      * to one of the values defined in the "categoryEncoding" object and be
      * interpreted as the matching category.
      */
-    public Ranges ranges;
+    public Map<String,NdArray> ranges;
 
     public Coverage() {
+        type = "Coverage";
     }
 
     @Override
@@ -127,20 +128,20 @@ public final class Coverage extends CoverageJsonObject {
                 ranges);
     }
 
-    public static class DomainDeserializer implements 
JsonbDeserializer<Object> {
+    public static class DomainDeserializer extends JsonDeserializer<Object> {
         public DomainDeserializer() {
         }
 
         @Override
-        public Object deserialize(JsonParser parser, DeserializationContext 
ctx, Type rtType) {
-            final JsonParser.Event event = parser.next();
-            if (event == JsonParser.Event.START_OBJECT) {
-                // Deserialize inner object
-                return ctx.deserialize(Domain.class, parser);
-            } else if (event == JsonParser.Event.VALUE_STRING) {
-                return parser.getString();
+        public Object deserialize(JsonParser parser, DeserializationContext 
ctxt) throws IOException, JacksonException {
+            final JsonNode rootNode = ctxt.readTree(parser);
+
+            if (rootNode.isTextual()) {
+                return rootNode.textValue();
+            } else if (rootNode.isObject()) {
+                return ctxt.readTreeAsValue(rootNode, Domain.class);
             } else {
-                throw new JsonbException("Unexpected json element");
+                throw new IOException("Unexpected json element");
             }
         }
     }
diff --git 
a/incubator/src/org.apache.sis.storage.json/main/org/apache/sis/storage/coveragejson/binding/CoverageCollection.java
 
b/incubator/src/org.apache.sis.storage.json/main/org/apache/sis/storage/coveragejson/binding/CoverageCollection.java
index decbb37efc..cfc7210417 100644
--- 
a/incubator/src/org.apache.sis.storage.json/main/org/apache/sis/storage/coveragejson/binding/CoverageCollection.java
+++ 
b/incubator/src/org.apache.sis.storage.json/main/org/apache/sis/storage/coveragejson/binding/CoverageCollection.java
@@ -16,10 +16,10 @@
  */
 package org.apache.sis.storage.coveragejson.binding;
 
+import com.fasterxml.jackson.annotation.JsonPropertyOrder;
 import java.util.List;
+import java.util.Map;
 import java.util.Objects;
-import jakarta.json.bind.annotation.JsonbNillable;
-import jakarta.json.bind.annotation.JsonbPropertyOrder;
 
 
 /**
@@ -28,8 +28,7 @@ import jakarta.json.bind.annotation.JsonbPropertyOrder;
  *
  * @author Johann Sorel (Geomatys)
  */
-@JsonbNillable(false)
-@JsonbPropertyOrder({"type","domainType","parameters","parameterGroups","referencing","coverages"})
+@JsonPropertyOrder({"type","domainType","parameters","parameterGroups","referencing","coverages"})
 public final class CoverageCollection extends CoverageJsonObject {
     /**
      * COPIED FROM OGC SPECIFICATION (TODO: ADAPT):
@@ -50,7 +49,7 @@ public final class CoverageCollection extends 
CoverageJsonObject {
      * where the value is an object where each member has as name a short
      * identifier and as value a parameter object.
      */
-    public Parameter parameters;
+    public Map<String,Parameter> parameters;
 
     /**
      * COPIED FROM OGC SPECIFICATION (TODO: ADAPT):
@@ -75,6 +74,7 @@ public final class CoverageCollection extends 
CoverageJsonObject {
     public List<Coverage> coverages;
 
     public CoverageCollection() {
+        type = "CoverageCollection";
     }
 
     @Override
diff --git 
a/incubator/src/org.apache.sis.storage.json/main/org/apache/sis/storage/coveragejson/binding/CoverageJsonObject.java
 
b/incubator/src/org.apache.sis.storage.json/main/org/apache/sis/storage/coveragejson/binding/CoverageJsonObject.java
index a2624aa3df..a48d961ab8 100644
--- 
a/incubator/src/org.apache.sis.storage.json/main/org/apache/sis/storage/coveragejson/binding/CoverageJsonObject.java
+++ 
b/incubator/src/org.apache.sis.storage.json/main/org/apache/sis/storage/coveragejson/binding/CoverageJsonObject.java
@@ -16,9 +16,10 @@
  */
 package org.apache.sis.storage.coveragejson.binding;
 
-import jakarta.json.bind.annotation.JsonbSubtype;
-import jakarta.json.bind.annotation.JsonbTypeInfo;
-
+import com.fasterxml.jackson.annotation.JsonSubTypes;
+import com.fasterxml.jackson.annotation.JsonSubTypes.Type;
+import com.fasterxml.jackson.annotation.JsonTypeInfo;
+import org.apache.sis.storage.json.DataTransferObject;
 
 /**
  * COPIED FROM OGC SPECIFICATION (TODO: ADAPT):
@@ -37,22 +38,26 @@ import jakarta.json.bind.annotation.JsonbTypeInfo;
  *
  * @author Johann Sorel (Geomatys)
  */
-@JsonbTypeInfo ( key = "type",value = {
-    @JsonbSubtype(alias = "Coverage", type = Coverage.class),
-    @JsonbSubtype(alias = "CoverageCollection", type = 
CoverageCollection.class),
-    @JsonbSubtype(alias = "Domain", type = Domain.class),
-    @JsonbSubtype(alias = "NdArray", type = NdArray.class),
-    @JsonbSubtype(alias = "Parameter", type = Parameter.class),
-    @JsonbSubtype(alias = "ParameterGroup", type = ParameterGroup.class),
+@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, include = 
JsonTypeInfo.As.EXISTING_PROPERTY, property = "type")
+@JsonSubTypes ( {
+    @Type(name = "Coverage", value = Coverage.class),
+    @Type(name = "CoverageCollection", value = CoverageCollection.class),
+    @Type(name = "Domain", value = Domain.class),
+    @Type(name = "NdArray", value = NdArray.class),
+    @Type(name = "Parameter", value = Parameter.class),
+    @Type(name = "ParameterGroup", value = ParameterGroup.class),
 
     //system subtypes
-    @JsonbSubtype(alias = "GeographicCRS", type = GeographicCRS.class),
-    @JsonbSubtype(alias = "ProjectedCRS", type = ProjectedCRS.class),
-    @JsonbSubtype(alias = "IdentifierRS", type = IdentifierRS.class),
-    @JsonbSubtype(alias = "VerticalCRS", type = VerticalCRS.class),
-    @JsonbSubtype(alias = "TemporalRS", type = TemporalRS.class)
+    @Type(name = "GeographicCRS", value = GeographicCRS.class),
+    @Type(name = "ProjectedCRS", value = ProjectedCRS.class),
+    @Type(name = "IdentifierRS", value = IdentifierRS.class),
+    @Type(name = "VerticalCRS", value = VerticalCRS.class),
+    @Type(name = "TemporalRS", value = TemporalRS.class)
 })
-public class CoverageJsonObject extends Dictionary<Object> {
+public class CoverageJsonObject extends DataTransferObject {
+
+    public String type;
+
     public CoverageJsonObject() {
     }
 }
diff --git 
a/incubator/src/org.apache.sis.storage.json/main/org/apache/sis/storage/coveragejson/binding/Dictionary.java
 
b/incubator/src/org.apache.sis.storage.json/main/org/apache/sis/storage/coveragejson/binding/Dictionary.java
deleted file mode 100644
index 2d410a80bc..0000000000
--- 
a/incubator/src/org.apache.sis.storage.json/main/org/apache/sis/storage/coveragejson/binding/Dictionary.java
+++ /dev/null
@@ -1,73 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements.  See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You 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 org.apache.sis.storage.coveragejson.binding;
-
-import java.util.LinkedHashMap;
-import java.util.Map;
-import jakarta.json.bind.Jsonb;
-import jakarta.json.bind.JsonbBuilder;
-import jakarta.json.bind.annotation.JsonbTransient;
-
-
-/**
- * @author Johann Sorel (Geomatys)
- */
-public class Dictionary<T> {
-
-    /**
-     * could be anything.
-     * TODO find how to cath any other property as in Jackson @JsonAnyGetter 
and Johnzon @JohnzonAny
-     */
-    @JsonbTransient
-    public final LinkedHashMap<String, T> any;
-
-    public Dictionary() {
-        any = new LinkedHashMap<>();
-    }
-
-    public final Map<String, T> getAny() {
-        return this.any;
-    }
-
-    public final void setAnyProperty(String name, T value) {
-        this.any.put(name, value);
-    }
-
-    @Override
-    public String toString() {
-        try (Jsonb jsonb = JsonbBuilder.create()) {
-            return jsonb.toJson(this);
-        } catch (Exception ex) {
-            ex.printStackTrace();
-            return getClass().getName() + " : to_string_exception" + 
ex.getMessage();
-        }
-    }
-
-    @Override
-    public boolean equals(Object other) {
-        if (other == this) return true;
-        if (!(other instanceof Dictionary<?>)) return false;
-
-        final var cdt = ((Dictionary<?>) other);
-        return any.equals(cdt.any);
-    }
-
-    @Override
-    public int hashCode() {
-        return any.hashCode();
-    }
-}
diff --git 
a/incubator/src/org.apache.sis.storage.json/main/org/apache/sis/storage/coveragejson/binding/Domain.java
 
b/incubator/src/org.apache.sis.storage.json/main/org/apache/sis/storage/coveragejson/binding/Domain.java
index 31ed0a64e9..4f294fdf69 100644
--- 
a/incubator/src/org.apache.sis.storage.json/main/org/apache/sis/storage/coveragejson/binding/Domain.java
+++ 
b/incubator/src/org.apache.sis.storage.json/main/org/apache/sis/storage/coveragejson/binding/Domain.java
@@ -16,10 +16,9 @@
  */
 package org.apache.sis.storage.coveragejson.binding;
 
+import com.fasterxml.jackson.annotation.JsonPropertyOrder;
 import java.util.List;
 import java.util.Objects;
-import jakarta.json.bind.annotation.JsonbNillable;
-import jakarta.json.bind.annotation.JsonbPropertyOrder;
 
 
 /**
@@ -29,8 +28,7 @@ import jakarta.json.bind.annotation.JsonbPropertyOrder;
  *
  * @author Johann Sorel (Geomatys)
  */
-@JsonbNillable(false)
-@JsonbPropertyOrder({"type","domainType","axes","referencing"})
+@JsonPropertyOrder({"type","domainType","axes","referencing"})
 public final class Domain extends CoverageJsonObject {
 
     public static final String DOMAINTYPE_GRID = "Grid";
@@ -75,6 +73,7 @@ public final class Domain extends CoverageJsonObject {
     public List<ReferenceSystemConnection> referencing;
 
     public Domain() {
+        type = "Domain";
     }
 
     @Override
diff --git 
a/incubator/src/org.apache.sis.storage.json/main/org/apache/sis/storage/coveragejson/binding/GeographicCRS.java
 
b/incubator/src/org.apache.sis.storage.json/main/org/apache/sis/storage/coveragejson/binding/GeographicCRS.java
index f88f625fd6..7145c8bb46 100644
--- 
a/incubator/src/org.apache.sis.storage.json/main/org/apache/sis/storage/coveragejson/binding/GeographicCRS.java
+++ 
b/incubator/src/org.apache.sis.storage.json/main/org/apache/sis/storage/coveragejson/binding/GeographicCRS.java
@@ -16,9 +16,8 @@
  */
 package org.apache.sis.storage.coveragejson.binding;
 
+import com.fasterxml.jackson.annotation.JsonPropertyOrder;
 import java.util.Objects;
-import jakarta.json.bind.annotation.JsonbNillable;
-import jakarta.json.bind.annotation.JsonbPropertyOrder;
 
 
 /**
@@ -39,8 +38,7 @@ import jakarta.json.bind.annotation.JsonbPropertyOrder;
  *
  * @author Johann Sorel (Geomatys)
  */
-@JsonbNillable(false)
-@JsonbPropertyOrder({"type","id","description"})
+@JsonPropertyOrder({"type","id","description"})
 public final class GeographicCRS extends CoverageJsonObject {
     /**
      * COPIED FROM OGC SPECIFICATION (TODO: ADAPT):
@@ -57,6 +55,7 @@ public final class GeographicCRS extends CoverageJsonObject {
     public I18N description;
 
     public GeographicCRS() {
+        type = "GeographicCRS";
     }
 
     @Override
diff --git 
a/incubator/src/org.apache.sis.storage.json/main/org/apache/sis/storage/coveragejson/binding/I18N.java
 
b/incubator/src/org.apache.sis.storage.json/main/org/apache/sis/storage/coveragejson/binding/I18N.java
index 04905ae3c6..e47187dc0d 100644
--- 
a/incubator/src/org.apache.sis.storage.json/main/org/apache/sis/storage/coveragejson/binding/I18N.java
+++ 
b/incubator/src/org.apache.sis.storage.json/main/org/apache/sis/storage/coveragejson/binding/I18N.java
@@ -16,19 +16,9 @@
  */
 package org.apache.sis.storage.coveragejson.binding;
 
-import java.lang.reflect.Type;
 import java.util.Locale;
-import java.util.Map;
-import jakarta.json.bind.annotation.JsonbTypeDeserializer;
-import jakarta.json.bind.annotation.JsonbTypeSerializer;
-import jakarta.json.bind.serializer.DeserializationContext;
-import jakarta.json.bind.serializer.JsonbDeserializer;
-import jakarta.json.bind.serializer.JsonbSerializer;
-import jakarta.json.bind.serializer.SerializationContext;
-import jakarta.json.stream.JsonGenerator;
-import jakarta.json.stream.JsonParser;
 import org.opengis.util.InternationalString;
-import org.apache.sis.storage.coveragejson.binding.I18N.Serializer;
+import org.apache.sis.storage.json.DataTransferObject;
 
 
 /**
@@ -38,9 +28,7 @@ import 
org.apache.sis.storage.coveragejson.binding.I18N.Serializer;
  *
  * @author Johann Sorel (Geomatys)
  */
-@JsonbTypeDeserializer(I18N.Deserializer.class)
-@JsonbTypeSerializer(Serializer.class)
-public final class I18N extends Dictionary<String> implements 
InternationalString {
+public final class I18N extends DataTransferObject implements 
InternationalString {
 
     public static final String UNDETERMINED = "und";
 
@@ -48,25 +36,22 @@ public final class I18N extends Dictionary<String> 
implements InternationalStrin
     }
 
     public I18N(String lang, String text) {
-        setAnyProperty(lang, text);
+        setOtherField(lang, text);
     }
 
     private String getDefault() {
-        String str = any.get(UNDETERMINED);
-        if (str == null && !any.isEmpty()) str = 
any.get(any.keySet().iterator().next());
+        Object str = unknownFields.get(UNDETERMINED);
+        if (str == null && !unknownFields.isEmpty()) str = 
unknownFields.get(unknownFields.keySet().iterator().next());
         if (str == null) str = "";
-        return str;
-    }
-
-    public String toString() {
-        return getDefault();
+        return String.valueOf(str);
     }
 
     @Override
     public String toString(Locale locale) {
-        String str = any.get(locale.getLanguage());
-        if (str == null) str = any.get(locale.getISO3Language());
-        return getDefault();
+        Object str = unknownFields.get(locale.getLanguage());
+        if (str == null) str = unknownFields.get(locale.getISO3Language());
+        if (str == null) getDefault();
+        return String.valueOf(str);
     }
 
     @Override
@@ -89,37 +74,4 @@ public final class I18N extends Dictionary<String> 
implements InternationalStrin
         return getDefault().compareTo(o.toString());
     }
 
-    public static class Deserializer implements JsonbDeserializer<I18N> {
-        public Deserializer() {
-        }
-
-        @Override
-        public I18N deserialize(JsonParser parser, DeserializationContext ctx, 
Type rtType) {
-            final I18N candidate = new I18N();
-            while (parser.hasNext()) {
-                final JsonParser.Event event = parser.next();
-                if (event == JsonParser.Event.KEY_NAME) {
-                    // Deserialize inner object
-                    final String name = parser.getString();
-                    String value = ctx.deserialize(String.class, parser);
-                    candidate.setAnyProperty(name, value);
-                }
-            }
-            return candidate;
-        }
-    }
-
-    public static class Serializer implements JsonbSerializer<I18N> {
-        public Serializer() {
-        }
-
-        @Override
-        public void serialize(I18N ranges, JsonGenerator jg, 
SerializationContext sc) {
-            jg.writeStartObject();
-            for (Map.Entry<String,String> entry : ranges.any.entrySet()) {
-                jg.write(entry.getKey(), entry.getValue());
-            }
-            jg.writeEnd();
-        }
-    }
 }
diff --git 
a/incubator/src/org.apache.sis.storage.json/main/org/apache/sis/storage/coveragejson/binding/IdentifierRS.java
 
b/incubator/src/org.apache.sis.storage.json/main/org/apache/sis/storage/coveragejson/binding/IdentifierRS.java
index a418b23f27..14e7400168 100644
--- 
a/incubator/src/org.apache.sis.storage.json/main/org/apache/sis/storage/coveragejson/binding/IdentifierRS.java
+++ 
b/incubator/src/org.apache.sis.storage.json/main/org/apache/sis/storage/coveragejson/binding/IdentifierRS.java
@@ -16,9 +16,9 @@
  */
 package org.apache.sis.storage.coveragejson.binding;
 
+import com.fasterxml.jackson.annotation.JsonPropertyOrder;
+import java.util.Map;
 import java.util.Objects;
-import jakarta.json.bind.annotation.JsonbNillable;
-import jakarta.json.bind.annotation.JsonbPropertyOrder;
 
 
 /**
@@ -29,8 +29,7 @@ import jakarta.json.bind.annotation.JsonbPropertyOrder;
  *
  * @author Johann Sorel (Geomatys)
  */
-@JsonbNillable(false)
-@JsonbPropertyOrder({"type","id","label","description","targetConcept","identifiers"})
+@JsonPropertyOrder({"type","id","label","description","targetConcept","identifiers"})
 public final class IdentifierRS extends CoverageJsonObject {
     /**
      * COPIED FROM OGC SPECIFICATION (TODO: ADAPT):
@@ -64,9 +63,10 @@ public final class IdentifierRS extends CoverageJsonObject {
      * COPIED FROM OGC SPECIFICATION (TODO: ADAPT):
      * An identifier RS object MAY have a member "identifiers".
      */
-    public Identifiers identifiers;
+    public Map<String,TargetConcept> identifiers;
 
     public IdentifierRS() {
+        type = "IdentifierRS";
     }
 
     @Override
diff --git 
a/incubator/src/org.apache.sis.storage.json/main/org/apache/sis/storage/coveragejson/binding/Identifiers.java
 
b/incubator/src/org.apache.sis.storage.json/main/org/apache/sis/storage/coveragejson/binding/Identifiers.java
deleted file mode 100644
index daec78fc6b..0000000000
--- 
a/incubator/src/org.apache.sis.storage.json/main/org/apache/sis/storage/coveragejson/binding/Identifiers.java
+++ /dev/null
@@ -1,31 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements.  See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You 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 org.apache.sis.storage.coveragejson.binding;
-
-
-/**
- * COPIED FROM OGC SPECIFICATION (TODO: ADAPT):
- * An identifier RS object MAY have a member "identifiers" where the value is
- * an object where each key is an identifier referenced by the identifier RS
- * and each value an object describing the referenced concept, equal to 
"targetConcept".
- *
- * @author Johann Sorel (Geomatys)
- */
-public final class Identifiers extends Dictionary<TargetConcept> {
-    public Identifiers() {
-    }
-}
diff --git 
a/incubator/src/org.apache.sis.storage.json/main/org/apache/sis/storage/coveragejson/binding/NdArray.java
 
b/incubator/src/org.apache.sis.storage.json/main/org/apache/sis/storage/coveragejson/binding/NdArray.java
index 197030b6ac..706034aab9 100644
--- 
a/incubator/src/org.apache.sis.storage.json/main/org/apache/sis/storage/coveragejson/binding/NdArray.java
+++ 
b/incubator/src/org.apache.sis.storage.json/main/org/apache/sis/storage/coveragejson/binding/NdArray.java
@@ -16,11 +16,10 @@
  */
 package org.apache.sis.storage.coveragejson.binding;
 
+import com.fasterxml.jackson.annotation.JsonPropertyOrder;
 import java.util.Arrays;
 import java.util.List;
 import java.util.Objects;
-import jakarta.json.bind.annotation.JsonbNillable;
-import jakarta.json.bind.annotation.JsonbPropertyOrder;
 
 
 /**
@@ -38,8 +37,7 @@ import jakarta.json.bind.annotation.JsonbPropertyOrder;
  *
  * @author Johann Sorel (Geomatys)
  */
-@JsonbNillable(false)
-@JsonbPropertyOrder({"type","dataType","axisNames","shape","values"})
+@JsonPropertyOrder({"type","dataType","axisNames","shape","values"})
 public final class NdArray extends CoverageJsonObject {
 
     public static final String DATATYPE_FLOAT = "float";
@@ -90,6 +88,7 @@ public final class NdArray extends CoverageJsonObject {
     public List<Object> values; //because of null and string values
 
     public NdArray() {
+        type = "NdArray";
     }
 
     @Override
diff --git 
a/incubator/src/org.apache.sis.storage.json/main/org/apache/sis/storage/coveragejson/binding/ObservedProperty.java
 
b/incubator/src/org.apache.sis.storage.json/main/org/apache/sis/storage/coveragejson/binding/ObservedProperty.java
index 3eee91a537..d2fab460ba 100644
--- 
a/incubator/src/org.apache.sis.storage.json/main/org/apache/sis/storage/coveragejson/binding/ObservedProperty.java
+++ 
b/incubator/src/org.apache.sis.storage.json/main/org/apache/sis/storage/coveragejson/binding/ObservedProperty.java
@@ -16,10 +16,10 @@
  */
 package org.apache.sis.storage.coveragejson.binding;
 
+import com.fasterxml.jackson.annotation.JsonPropertyOrder;
 import java.util.List;
 import java.util.Objects;
-import jakarta.json.bind.annotation.JsonbNillable;
-import jakarta.json.bind.annotation.JsonbPropertyOrder;
+import org.apache.sis.storage.json.DataTransferObject;
 
 
 /**
@@ -29,9 +29,8 @@ import jakarta.json.bind.annotation.JsonbPropertyOrder;
  *
  * @author Johann Sorel (Geomatys)
  */
-@JsonbNillable(false)
-@JsonbPropertyOrder({"id","label","description","categories"})
-public final class ObservedProperty extends Dictionary<Object> {
+@JsonPropertyOrder({"id","label","description","categories"})
+public final class ObservedProperty extends DataTransferObject {
     /**
      * COPIED FROM OGC SPECIFICATION (TODO: ADAPT):
      * If given, the value of "id" MUST be a string and SHOULD be a common
diff --git 
a/incubator/src/org.apache.sis.storage.json/main/org/apache/sis/storage/coveragejson/binding/Parameter.java
 
b/incubator/src/org.apache.sis.storage.json/main/org/apache/sis/storage/coveragejson/binding/Parameter.java
index bf83f26a30..16af26a053 100644
--- 
a/incubator/src/org.apache.sis.storage.json/main/org/apache/sis/storage/coveragejson/binding/Parameter.java
+++ 
b/incubator/src/org.apache.sis.storage.json/main/org/apache/sis/storage/coveragejson/binding/Parameter.java
@@ -16,9 +16,8 @@
  */
 package org.apache.sis.storage.coveragejson.binding;
 
+import com.fasterxml.jackson.annotation.JsonPropertyOrder;
 import java.util.Objects;
-import jakarta.json.bind.annotation.JsonbNillable;
-import jakarta.json.bind.annotation.JsonbPropertyOrder;
 
 
 /**
@@ -27,8 +26,7 @@ import jakarta.json.bind.annotation.JsonbPropertyOrder;
  *
  * @author Johann Sorel (Geomatys)
  */
-@JsonbNillable(false)
-@JsonbPropertyOrder({"type","id","label","description","unit","observedProperty","categoryEncoding"})
+@JsonPropertyOrder({"type","id","label","description","unit","observedProperty","categoryEncoding"})
 public final class Parameter extends CoverageJsonObject {
     /**
      * COPIED FROM OGC SPECIFICATION (TODO: ADAPT):
@@ -75,6 +73,7 @@ public final class Parameter extends CoverageJsonObject {
     public CategoryEncoding categoryEncoding;
 
     public Parameter() {
+        type = "Parameter";
     }
 
     @Override
diff --git 
a/incubator/src/org.apache.sis.storage.json/main/org/apache/sis/storage/coveragejson/binding/ParameterGroup.java
 
b/incubator/src/org.apache.sis.storage.json/main/org/apache/sis/storage/coveragejson/binding/ParameterGroup.java
index 6ec5f23d04..b810f03f1f 100644
--- 
a/incubator/src/org.apache.sis.storage.json/main/org/apache/sis/storage/coveragejson/binding/ParameterGroup.java
+++ 
b/incubator/src/org.apache.sis.storage.json/main/org/apache/sis/storage/coveragejson/binding/ParameterGroup.java
@@ -16,10 +16,9 @@
  */
 package org.apache.sis.storage.coveragejson.binding;
 
+import com.fasterxml.jackson.annotation.JsonPropertyOrder;
 import java.util.Arrays;
 import java.util.Objects;
-import jakarta.json.bind.annotation.JsonbNillable;
-import jakarta.json.bind.annotation.JsonbPropertyOrder;
 
 
 /**
@@ -28,8 +27,7 @@ import jakarta.json.bind.annotation.JsonbPropertyOrder;
  *
  * @author Johann Sorel (Geomatys)
  */
-@JsonbNillable(false)
-@JsonbPropertyOrder({"type","id","label","description","observedProperty","members"})
+@JsonPropertyOrder({"type","id","label","description","observedProperty","members"})
 public final class ParameterGroup extends CoverageJsonObject {
     /**
      * COPIED FROM OGC SPECIFICATION (TODO: ADAPT):
@@ -70,6 +68,7 @@ public final class ParameterGroup extends CoverageJsonObject {
     public String[] members;
 
     public ParameterGroup() {
+        type = "ParameterGroup";
     }
 
     @Override
diff --git 
a/incubator/src/org.apache.sis.storage.json/main/org/apache/sis/storage/coveragejson/binding/Parameters.java
 
b/incubator/src/org.apache.sis.storage.json/main/org/apache/sis/storage/coveragejson/binding/Parameters.java
deleted file mode 100644
index 999b2791db..0000000000
--- 
a/incubator/src/org.apache.sis.storage.json/main/org/apache/sis/storage/coveragejson/binding/Parameters.java
+++ /dev/null
@@ -1,78 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements.  See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You 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 org.apache.sis.storage.coveragejson.binding;
-
-import java.lang.reflect.Type;
-import java.util.Map;
-import jakarta.json.bind.annotation.JsonbTypeDeserializer;
-import jakarta.json.bind.annotation.JsonbTypeSerializer;
-import jakarta.json.bind.serializer.DeserializationContext;
-import jakarta.json.bind.serializer.JsonbDeserializer;
-import jakarta.json.bind.serializer.JsonbSerializer;
-import jakarta.json.bind.serializer.SerializationContext;
-import jakarta.json.stream.JsonGenerator;
-import jakarta.json.stream.JsonParser;
-import jakarta.json.stream.JsonParser.Event;
-import org.apache.sis.storage.coveragejson.binding.Parameters.Deserializer;
-import org.apache.sis.storage.coveragejson.binding.Parameters.Serializer;
-
-
-/**
- * Contains a map of parameter objects.
- *
- * @author Johann Sorel (Geomatys)
- */
-@JsonbTypeDeserializer(Deserializer.class)
-@JsonbTypeSerializer(Serializer.class)
-public final class Parameters extends Dictionary<Parameter> {
-    public Parameters() {
-    }
-
-    public static class Deserializer implements JsonbDeserializer<Parameters> {
-        public Deserializer() {
-        }
-
-        @Override
-        public Parameters deserialize(JsonParser parser, 
DeserializationContext ctx, Type rtType) {
-            final Parameters parameters = new Parameters();
-            while (parser.hasNext()) {
-                final Event event = parser.next();
-                if (event == JsonParser.Event.KEY_NAME) {
-                    // Deserialize inner object
-                    final String name = parser.getString();
-                    final Parameter value = ctx.deserialize(Parameter.class, 
parser);
-                    parameters.setAnyProperty(name, value);
-                }
-            }
-            return parameters;
-        }
-    }
-
-    public static class Serializer implements JsonbSerializer<Parameters> {
-        public Serializer() {
-        }
-
-        @Override
-        public void serialize(Parameters parameters, JsonGenerator jg, 
SerializationContext sc) {
-            jg.writeStartObject();
-            for (Map.Entry<String,Parameter> entry : 
parameters.any.entrySet()) {
-                sc.serialize(entry.getKey(), entry.getValue(), jg);
-            }
-            jg.writeEnd();
-        }
-    }
-}
diff --git 
a/incubator/src/org.apache.sis.storage.json/main/org/apache/sis/storage/coveragejson/binding/ProjectedCRS.java
 
b/incubator/src/org.apache.sis.storage.json/main/org/apache/sis/storage/coveragejson/binding/ProjectedCRS.java
index 0b156891f0..5d40e3f614 100644
--- 
a/incubator/src/org.apache.sis.storage.json/main/org/apache/sis/storage/coveragejson/binding/ProjectedCRS.java
+++ 
b/incubator/src/org.apache.sis.storage.json/main/org/apache/sis/storage/coveragejson/binding/ProjectedCRS.java
@@ -16,9 +16,8 @@
  */
 package org.apache.sis.storage.coveragejson.binding;
 
+import com.fasterxml.jackson.annotation.JsonPropertyOrder;
 import java.util.Objects;
-import jakarta.json.bind.annotation.JsonbNillable;
-import jakarta.json.bind.annotation.JsonbPropertyOrder;
 
 
 /**
@@ -31,8 +30,7 @@ import jakarta.json.bind.annotation.JsonbPropertyOrder;
  *
  * @author Johann Sorel (Geomatys)
  */
-@JsonbNillable(false)
-@JsonbPropertyOrder({"type","id","description"})
+@JsonPropertyOrder({"type","id","description"})
 public final class ProjectedCRS extends CoverageJsonObject {
     /**
      * COPIED FROM OGC SPECIFICATION (TODO: ADAPT):
@@ -49,6 +47,7 @@ public final class ProjectedCRS extends CoverageJsonObject {
     public I18N description;
 
     public ProjectedCRS() {
+        type = "ProjectedCRS";
     }
 
     @Override
diff --git 
a/incubator/src/org.apache.sis.storage.json/main/org/apache/sis/storage/coveragejson/binding/Ranges.java
 
b/incubator/src/org.apache.sis.storage.json/main/org/apache/sis/storage/coveragejson/binding/Ranges.java
deleted file mode 100644
index 6e16a9e1bf..0000000000
--- 
a/incubator/src/org.apache.sis.storage.json/main/org/apache/sis/storage/coveragejson/binding/Ranges.java
+++ /dev/null
@@ -1,74 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements.  See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You 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 org.apache.sis.storage.coveragejson.binding;
-
-import java.lang.reflect.Type;
-import java.util.Map.Entry;
-import jakarta.json.bind.annotation.JsonbTypeDeserializer;
-import jakarta.json.bind.annotation.JsonbTypeSerializer;
-import jakarta.json.bind.serializer.DeserializationContext;
-import jakarta.json.bind.serializer.JsonbDeserializer;
-import jakarta.json.bind.serializer.JsonbSerializer;
-import jakarta.json.bind.serializer.SerializationContext;
-import jakarta.json.stream.JsonGenerator;
-import jakarta.json.stream.JsonParser;
-
-
-/**
- *
- * @author Johann Sorel (Geomatys)
- */
-@JsonbTypeDeserializer(Ranges.Deserializer.class)
-@JsonbTypeSerializer(Ranges.Serializer.class)
-public final class Ranges extends Dictionary<NdArray> {
-    public Ranges() {
-    }
-
-    public static class Deserializer implements JsonbDeserializer<Ranges> {
-        public Deserializer() {
-        }
-
-        @Override
-        public Ranges deserialize(JsonParser parser, DeserializationContext 
ctx, Type rtType) {
-            final Ranges candidate = new Ranges();
-            while (parser.hasNext()) {
-                final JsonParser.Event event = parser.next();
-                if (event == JsonParser.Event.KEY_NAME) {
-                    // Deserialize inner object
-                    final String name = parser.getString();
-                    final NdArray value = ctx.deserialize(NdArray.class, 
parser);
-                    candidate.setAnyProperty(name, value);
-                }
-            }
-            return candidate;
-        }
-    }
-
-    public static class Serializer implements JsonbSerializer<Ranges> {
-        public Serializer() {
-        }
-
-        @Override
-        public void serialize(Ranges ranges, JsonGenerator jg, 
SerializationContext sc) {
-            jg.writeStartObject();
-            for (Entry<String,NdArray> entry : ranges.any.entrySet()) {
-                sc.serialize(entry.getKey(), entry.getValue(), jg);
-            }
-            jg.writeEnd();
-        }
-    }
-}
diff --git 
a/incubator/src/org.apache.sis.storage.json/main/org/apache/sis/storage/coveragejson/binding/ReferenceSystemConnection.java
 
b/incubator/src/org.apache.sis.storage.json/main/org/apache/sis/storage/coveragejson/binding/ReferenceSystemConnection.java
index 35e17c7fc6..e6775da60d 100644
--- 
a/incubator/src/org.apache.sis.storage.json/main/org/apache/sis/storage/coveragejson/binding/ReferenceSystemConnection.java
+++ 
b/incubator/src/org.apache.sis.storage.json/main/org/apache/sis/storage/coveragejson/binding/ReferenceSystemConnection.java
@@ -16,10 +16,10 @@
  */
 package org.apache.sis.storage.coveragejson.binding;
 
+import com.fasterxml.jackson.annotation.JsonPropertyOrder;
 import java.util.List;
 import java.util.Objects;
-import jakarta.json.bind.annotation.JsonbNillable;
-import jakarta.json.bind.annotation.JsonbPropertyOrder;
+import org.apache.sis.storage.json.DataTransferObject;
 
 
 /**
@@ -30,9 +30,8 @@ import jakarta.json.bind.annotation.JsonbPropertyOrder;
  *
  * @author Johann Sorel (Geomatys)
  */
-@JsonbNillable(false)
-@JsonbPropertyOrder({"coordinates","system"})
-public final class ReferenceSystemConnection extends Dictionary<Object> {
+@JsonPropertyOrder({"coordinates","system"})
+public final class ReferenceSystemConnection extends DataTransferObject {
     /**
      * COPIED FROM OGC SPECIFICATION (TODO: ADAPT):
      * A reference system connection object MUST have a member "coordinates"
diff --git 
a/incubator/src/org.apache.sis.storage.json/main/org/apache/sis/storage/coveragejson/binding/Symbol.java
 
b/incubator/src/org.apache.sis.storage.json/main/org/apache/sis/storage/coveragejson/binding/Symbol.java
index c7f1c4d7e6..8ffbe3a833 100644
--- 
a/incubator/src/org.apache.sis.storage.json/main/org/apache/sis/storage/coveragejson/binding/Symbol.java
+++ 
b/incubator/src/org.apache.sis.storage.json/main/org/apache/sis/storage/coveragejson/binding/Symbol.java
@@ -16,18 +16,17 @@
  */
 package org.apache.sis.storage.coveragejson.binding;
 
+import com.fasterxml.jackson.annotation.JsonPropertyOrder;
 import java.util.Objects;
-import jakarta.json.bind.annotation.JsonbNillable;
-import jakarta.json.bind.annotation.JsonbPropertyOrder;
+import org.apache.sis.storage.json.DataTransferObject;
 
 
 /**
  *
  * @author Johann Sorel (Geomatys)
  */
-@JsonbNillable(false)
-@JsonbPropertyOrder({"value","type"})
-public final class Symbol extends Dictionary<Object> {
+@JsonPropertyOrder({"value","type"})
+public final class Symbol extends DataTransferObject {
     /**
      * COPIED FROM OGC SPECIFICATION (TODO: ADAPT):
      * "value" is the symbolic unit notation
diff --git 
a/incubator/src/org.apache.sis.storage.json/main/org/apache/sis/storage/coveragejson/binding/TargetConcept.java
 
b/incubator/src/org.apache.sis.storage.json/main/org/apache/sis/storage/coveragejson/binding/TargetConcept.java
index ac777633ce..7c6b036cf3 100644
--- 
a/incubator/src/org.apache.sis.storage.json/main/org/apache/sis/storage/coveragejson/binding/TargetConcept.java
+++ 
b/incubator/src/org.apache.sis.storage.json/main/org/apache/sis/storage/coveragejson/binding/TargetConcept.java
@@ -16,9 +16,9 @@
  */
 package org.apache.sis.storage.coveragejson.binding;
 
+import com.fasterxml.jackson.annotation.JsonPropertyOrder;
 import java.util.Objects;
-import jakarta.json.bind.annotation.JsonbNillable;
-import jakarta.json.bind.annotation.JsonbPropertyOrder;
+import org.apache.sis.storage.json.DataTransferObject;
 
 
 /**
@@ -29,9 +29,8 @@ import jakarta.json.bind.annotation.JsonbPropertyOrder;
  *
  * @author Johann Sorel (Geomatys)
  */
-@JsonbNillable(false)
-@JsonbPropertyOrder({"label","description"})
-public final class TargetConcept extends Dictionary<Object> {
+@JsonPropertyOrder({"label","description"})
+public final class TargetConcept extends DataTransferObject {
 
     public I18N label;
     public I18N description;
diff --git 
a/incubator/src/org.apache.sis.storage.json/main/org/apache/sis/storage/coveragejson/binding/TemporalRS.java
 
b/incubator/src/org.apache.sis.storage.json/main/org/apache/sis/storage/coveragejson/binding/TemporalRS.java
index 3952e78ced..9532441d1d 100644
--- 
a/incubator/src/org.apache.sis.storage.json/main/org/apache/sis/storage/coveragejson/binding/TemporalRS.java
+++ 
b/incubator/src/org.apache.sis.storage.json/main/org/apache/sis/storage/coveragejson/binding/TemporalRS.java
@@ -16,9 +16,8 @@
  */
 package org.apache.sis.storage.coveragejson.binding;
 
+import com.fasterxml.jackson.annotation.JsonPropertyOrder;
 import java.util.Objects;
-import jakarta.json.bind.annotation.JsonbNillable;
-import jakarta.json.bind.annotation.JsonbPropertyOrder;
 
 
 /**
@@ -45,8 +44,7 @@ import jakarta.json.bind.annotation.JsonbPropertyOrder;
  *
  * @author Johann Sorel (Geomatys)
  */
-@JsonbNillable(false)
-@JsonbPropertyOrder({"type","calendar","timeScale"})
+@JsonPropertyOrder({"type","calendar","timeScale"})
 public final class TemporalRS extends CoverageJsonObject {
     /**
      * COPIED FROM OGC SPECIFICATION (TODO: ADAPT):
@@ -64,6 +62,7 @@ public final class TemporalRS extends CoverageJsonObject {
     public String timeScale;
 
     public TemporalRS() {
+        type = "TemporalRS";
     }
 
     @Override
diff --git 
a/incubator/src/org.apache.sis.storage.json/main/org/apache/sis/storage/coveragejson/binding/TileSet.java
 
b/incubator/src/org.apache.sis.storage.json/main/org/apache/sis/storage/coveragejson/binding/TileSet.java
index ba93b12cf3..eee64b2eca 100644
--- 
a/incubator/src/org.apache.sis.storage.json/main/org/apache/sis/storage/coveragejson/binding/TileSet.java
+++ 
b/incubator/src/org.apache.sis.storage.json/main/org/apache/sis/storage/coveragejson/binding/TileSet.java
@@ -16,19 +16,18 @@
  */
 package org.apache.sis.storage.coveragejson.binding;
 
+import com.fasterxml.jackson.annotation.JsonPropertyOrder;
 import java.util.Arrays;
 import java.util.Objects;
-import jakarta.json.bind.annotation.JsonbNillable;
-import jakarta.json.bind.annotation.JsonbPropertyOrder;
+import org.apache.sis.storage.json.DataTransferObject;
 
 
 /**
  *
  * @author Johann Sorel (Geomatys)
  */
-@JsonbNillable(false)
-@JsonbPropertyOrder({"tileShape","urlTemplate"})
-public final class TileSet extends Dictionary<Object> {
+@JsonPropertyOrder({"tileShape","urlTemplate"})
+public final class TileSet extends DataTransferObject {
     /**
      * COPIED FROM OGC SPECIFICATION (TODO: ADAPT):
      * A TileSet object MUST have a member with the name "tileShape" where
diff --git 
a/incubator/src/org.apache.sis.storage.json/main/org/apache/sis/storage/coveragejson/binding/TiledNdArray.java
 
b/incubator/src/org.apache.sis.storage.json/main/org/apache/sis/storage/coveragejson/binding/TiledNdArray.java
index 76b55c7fff..c229e821b8 100644
--- 
a/incubator/src/org.apache.sis.storage.json/main/org/apache/sis/storage/coveragejson/binding/TiledNdArray.java
+++ 
b/incubator/src/org.apache.sis.storage.json/main/org/apache/sis/storage/coveragejson/binding/TiledNdArray.java
@@ -16,10 +16,10 @@
  */
 package org.apache.sis.storage.coveragejson.binding;
 
+import com.fasterxml.jackson.annotation.JsonPropertyOrder;
 import java.util.Arrays;
 import java.util.Objects;
-import jakarta.json.bind.annotation.JsonbNillable;
-import jakarta.json.bind.annotation.JsonbPropertyOrder;
+import org.apache.sis.storage.json.DataTransferObject;
 
 
 /**
@@ -32,9 +32,8 @@ import jakarta.json.bind.annotation.JsonbPropertyOrder;
  *
  * @author Johann Sorel (Geomatys)
  */
-@JsonbNillable(false)
-@JsonbPropertyOrder({"dataType","shape","axisNames","tileSets"})
-public final class TiledNdArray extends Dictionary<Object> {
+@JsonPropertyOrder({"dataType","shape","axisNames","tileSets"})
+public final class TiledNdArray extends DataTransferObject {
     /**
      * COPIED FROM OGC SPECIFICATION (TODO: ADAPT):
      * A TiledNdArray object MUST have a member with the name "dataType" where
diff --git 
a/incubator/src/org.apache.sis.storage.json/main/org/apache/sis/storage/coveragejson/binding/Unit.java
 
b/incubator/src/org.apache.sis.storage.json/main/org/apache/sis/storage/coveragejson/binding/Unit.java
index 87b9490339..ce21b2f7d0 100644
--- 
a/incubator/src/org.apache.sis.storage.json/main/org/apache/sis/storage/coveragejson/binding/Unit.java
+++ 
b/incubator/src/org.apache.sis.storage.json/main/org/apache/sis/storage/coveragejson/binding/Unit.java
@@ -16,9 +16,9 @@
  */
 package org.apache.sis.storage.coveragejson.binding;
 
+import com.fasterxml.jackson.annotation.JsonPropertyOrder;
 import java.util.Objects;
-import jakarta.json.bind.annotation.JsonbNillable;
-import jakarta.json.bind.annotation.JsonbPropertyOrder;
+import org.apache.sis.storage.json.DataTransferObject;
 
 
 /**
@@ -28,9 +28,8 @@ import jakarta.json.bind.annotation.JsonbPropertyOrder;
  *
  * @author Johann Sorel (Geomatys)
  */
-@JsonbNillable(false)
-@JsonbPropertyOrder({"id","label","symbol"})
-public final class Unit extends Dictionary<Object> {
+@JsonPropertyOrder({"id","label","symbol"})
+public final class Unit extends DataTransferObject {
     /**
      * COPIED FROM OGC SPECIFICATION (TODO: ADAPT):
      * MAY have the member "id".
diff --git 
a/incubator/src/org.apache.sis.storage.json/main/org/apache/sis/storage/coveragejson/binding/VerticalCRS.java
 
b/incubator/src/org.apache.sis.storage.json/main/org/apache/sis/storage/coveragejson/binding/VerticalCRS.java
index 43b90e7f42..fec3a137d2 100644
--- 
a/incubator/src/org.apache.sis.storage.json/main/org/apache/sis/storage/coveragejson/binding/VerticalCRS.java
+++ 
b/incubator/src/org.apache.sis.storage.json/main/org/apache/sis/storage/coveragejson/binding/VerticalCRS.java
@@ -16,9 +16,8 @@
  */
 package org.apache.sis.storage.coveragejson.binding;
 
+import com.fasterxml.jackson.annotation.JsonPropertyOrder;
 import java.util.Objects;
-import jakarta.json.bind.annotation.JsonbNillable;
-import jakarta.json.bind.annotation.JsonbPropertyOrder;
 
 
 /**
@@ -28,8 +27,7 @@ import jakarta.json.bind.annotation.JsonbPropertyOrder;
  *
  * @author Johann Sorel (Geomatys)
  */
-@JsonbNillable(false)
-@JsonbPropertyOrder({"type","id","description"})
+@JsonPropertyOrder({"type","id","description"})
 public final class VerticalCRS extends CoverageJsonObject {
     /**
      * COPIED FROM OGC SPECIFICATION (TODO: ADAPT):
@@ -46,6 +44,7 @@ public final class VerticalCRS extends CoverageJsonObject {
     public I18N description;
 
     public VerticalCRS() {
+        type = "VerticalCRS";
     }
 
     @Override
diff --git 
a/incubator/src/org.apache.sis.storage.json/main/org/apache/sis/storage/json/DataTransferObject.java
 
b/incubator/src/org.apache.sis.storage.json/main/org/apache/sis/storage/json/DataTransferObject.java
new file mode 100644
index 0000000000..c336e4c05e
--- /dev/null
+++ 
b/incubator/src/org.apache.sis.storage.json/main/org/apache/sis/storage/json/DataTransferObject.java
@@ -0,0 +1,100 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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 org.apache.sis.storage.json;
+
+import com.fasterxml.jackson.annotation.JsonAnyGetter;
+import com.fasterxml.jackson.annotation.JsonAnySetter;
+import com.fasterxml.jackson.annotation.JsonInclude;
+import com.fasterxml.jackson.annotation.JsonInclude.Include;
+import com.fasterxml.jackson.core.JsonProcessingException;
+import com.fasterxml.jackson.databind.JsonNode;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ *
+ * @author Johann Sorel (Geomatys)
+ */
+@JsonInclude(Include.NON_EMPTY)
+public abstract class DataTransferObject {
+
+    private static final ObjectMapper MAPPER = new ObjectMapper();
+
+    /**
+     * Catch all unknown fields
+     */
+    protected final Map<String, Object> unknownFields = new HashMap<>();
+
+    /**
+     * All unknown fields
+     */
+    @JsonAnyGetter
+    public Map<String, Object> otherFields() {
+        return unknownFields;
+    }
+
+    @JsonAnySetter
+    public void setOtherField(String name, Object value) {
+        unknownFields.put(name, value);
+    }
+
+    /**
+     * Create a copy of this object.
+     * The copy is created by transforming the object to a JsonNode
+     * and back to a class instance using jackson Object mapper.
+     *
+     * @return json copy of this object.
+     * @throws JsonProcessingException
+     */
+    public DataTransferObject copy() throws JsonProcessingException {
+        return MAPPER.treeToValue(MAPPER.valueToTree(this), this.getClass());
+    }
+
+    /**
+     * Compare objects based on there JSON representation using jackson mapper.
+     * Objects are transformed to JSONNode and compared using JsonNode.equals
+     *
+     * @param obj to compare
+     * @return true if objects are equal
+     */
+    @Override
+    public boolean equals(Object obj) {
+        if (obj == null || !getClass().equals(obj.getClass())) return false;
+        final JsonNode node1 = MAPPER.valueToTree(obj);
+        final JsonNode node2 = MAPPER.valueToTree(this);
+        return node1.equals(node2);
+    }
+
+    /**
+     * @return class hashcode
+     */
+    @Override
+    public int hashCode() {
+        return getClass().hashCode();
+    }
+
+    @Override
+    public final String toString() {
+        try {
+            return 
MAPPER.writerWithDefaultPrettyPrinter().writeValueAsString(this);
+        } catch (JsonProcessingException ex) {
+            return this.getClass().getName() + " : [Error: could not map class 
instance as json]";
+        }
+    }
+
+}
diff --git 
a/incubator/src/org.apache.sis.storage.json/test/org/apache/sis/storage/coveragejson/binding/BindingTest.java
 
b/incubator/src/org.apache.sis.storage.json/test/org/apache/sis/storage/coveragejson/binding/BindingTest.java
index 4449517327..5f688ff6ca 100644
--- 
a/incubator/src/org.apache.sis.storage.json/test/org/apache/sis/storage/coveragejson/binding/BindingTest.java
+++ 
b/incubator/src/org.apache.sis.storage.json/test/org/apache/sis/storage/coveragejson/binding/BindingTest.java
@@ -16,10 +16,12 @@
  */
 package org.apache.sis.storage.coveragejson.binding;
 
+import com.fasterxml.jackson.databind.JsonNode;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.databind.SerializationFeature;
 import java.io.ByteArrayOutputStream;
 import java.io.IOException;
 import java.io.InputStream;
-import java.math.BigDecimal;
 import java.nio.charset.StandardCharsets;
 import java.util.ArrayList;
 import java.util.Arrays;
@@ -27,10 +29,6 @@ import java.util.HashMap;
 import java.util.LinkedHashMap;
 import java.util.List;
 import java.util.Map;
-import jakarta.json.JsonObject;
-import jakarta.json.bind.Jsonb;
-import jakarta.json.bind.JsonbBuilder;
-import org.eclipse.yasson.YassonConfig;
 
 // Test dependencies
 import org.junit.jupiter.api.AfterAll;
@@ -47,10 +45,11 @@ import static org.junit.jupiter.api.Assertions.*;
 @TestInstance(TestInstance.Lifecycle.PER_CLASS)
 public class BindingTest {
 
-    private final Jsonb jsonb;
+    private final ObjectMapper mapper;
 
     public BindingTest() {
-        jsonb = JsonbBuilder.create(new YassonConfig().withFormatting(true));
+        mapper = new ObjectMapper();
+        mapper.enable(SerializationFeature.INDENT_OUTPUT);
     }
 
     public static String readResource(String path) throws IOException {
@@ -70,18 +69,17 @@ public class BindingTest {
     private void compare(String jsonpath, Object expected) throws IOException {
         String json = readResource(jsonpath);
         //reformat it the same way.
-        JsonObject map = jsonb.fromJson(json, JsonObject.class);
-        String formattedJson = jsonb.toJson(map);
+        JsonNode map = mapper.readTree(json);
+        String formattedJson = mapper.writeValueAsString(map);
 
-        final Object candidate = jsonb.fromJson(json, expected.getClass());
+        final Object candidate = mapper.readValue(json, expected.getClass());
         expected.equals(candidate);
         assertEquals(expected, candidate);
-        assertEquals(formattedJson, jsonb.toJson(candidate));
+        assertEquals(formattedJson, mapper.writeValueAsString(candidate));
     }
 
     @AfterAll
     public void afterClass() throws Exception {
-        jsonb.close();
     }
 
     @Test
@@ -185,9 +183,9 @@ public class BindingTest {
         POTM.unit = new Unit(null,null,"°C");
         POTM.observedProperty = new 
ObservedProperty("http://vocab.nerc.ac.uk/standard_name/sea_water_potential_temperature/";,
 new I18N("en", "Sea Water Potential Temperature"), null, null);
 
-        final Parameters parameters = new Parameters();
-        parameters.setAnyProperty("PSAL", PSAL);
-        parameters.setAnyProperty("POTM", POTM);
+        final Map<String,Parameter> parameters = new LinkedHashMap<>();
+        parameters.put("PSAL", PSAL);
+        parameters.put("POTM", POTM);
 
         final NdArray PSALr = new NdArray();
         PSALr.dataType ="float";
@@ -201,9 +199,9 @@ public class BindingTest {
         POTMr.axisNames = new String[]{"z"};
         POTMr.values = asList(23.8, 23.7);
 
-        final Ranges ranges = new Ranges();
-        ranges.setAnyProperty("PSAL", PSALr);
-        ranges.setAnyProperty("POTM", POTMr);
+        final Map<String,NdArray> ranges = new LinkedHashMap<>();
+        ranges.put("PSAL", PSALr);
+        ranges.put("POTM", POTMr);
 
         final Coverage expected = new Coverage();
         expected.domain = domain;
@@ -220,9 +218,9 @@ public class BindingTest {
         final List<Object> lst = new ArrayList<>(array.length);
         for (int i=0;i<array.length;i++) {
             if (array[i] instanceof Integer v) {
-                lst.add(BigDecimal.valueOf(v));
+                lst.add(v);
             } else if (array[i] instanceof Double v) {
-                lst.add(BigDecimal.valueOf(v));
+                lst.add(v);
             } else {
                 lst.add(array[i]);
             }
diff --git a/netbeans-project/ivy.xml b/netbeans-project/ivy.xml
index ff5f29fe8f..abdcbe449f 100644
--- a/netbeans-project/ivy.xml
+++ b/netbeans-project/ivy.xml
@@ -37,5 +37,10 @@
         <dependency org="gov.nist.math"          name="jama"                   
 rev="1.0.3"/>
         <dependency org="net.sf.geographiclib"   name="GeographicLib-Java"     
 rev="2.0"/>
         <dependency org="org.xerial"             name="sqlite-jdbc"            
 rev="3.45.1.0"/>
+        <dependency org="org.xerial"             name="sqlite-jdbc"            
 rev="3.45.1.0"/>
+        <dependency org="org.xerial"             name="sqlite-jdbc"            
 rev="3.45.1.0"/>
+        <dependency org="com.fasterxml.jackson.core" name="jackson-core"       
 rev="2.22.1"/>
+        <dependency org="com.fasterxml.jackson.core" 
name="jackson-annotations" rev="2.22"/>
+        <dependency org="com.fasterxml.jackson.core" name="jackson-databind"   
 rev="2.22.1"/>
     </dependencies>
 </ivy-module>

Reply via email to