├── .gitattributes ├── .gitignore ├── .travis.yml ├── README.md ├── pom.xml ├── release-notes ├── CREDITS-2.x └── VERSION-2.x └── src ├── main ├── java │ └── com │ │ └── fasterxml │ │ └── jackson │ │ └── datatype │ │ └── jsr353 │ │ ├── JSR353Module.java │ │ ├── JsonMergePatchDeserializer.java │ │ ├── JsonPatchDeserializer.java │ │ ├── JsonValueDeserializer.java │ │ ├── JsonValueSerializer.java │ │ └── PackageVersion.java.in └── resources │ ├── LICENSE │ └── META-INF │ └── services │ └── com.fasterxml.jackson.databind.Module ├── moditect └── module-info.java └── test └── java └── com └── fasterxml └── jackson └── datatype └── jsr353 ├── JsonMergePatchDeserializationTest.java ├── JsonPatchDeserializationTest.java ├── JsonValueDeserializationTest.java ├── JsonValueSerializationTest.java ├── PolymorphicTest.java └── TestBase.java /.gitattributes: -------------------------------------------------------------------------------- 1 | # Do not merge `pom.xml` from older version, as it will typically conflict 2 | 3 | pom.xml merge=ours 4 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # use glob syntax. 2 | syntax: glob 3 | *.class 4 | *~ 5 | *.bak 6 | *.off 7 | *.old 8 | .DS_Store 9 | 10 | # building 11 | target 12 | 13 | # Eclipse 14 | .classpath 15 | .project 16 | .settings 17 | 18 | # IDEA 19 | *.iml 20 | *.ipr 21 | *.iws 22 | .idea 23 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: java 2 | 3 | jdk: 4 | - openjdk8 5 | - openjdk11 6 | 7 | # whitelist 8 | branches: 9 | only: 10 | - master 11 | - "2.11" 12 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | **NOTE**: This module has become part of [Jackson Misc Datatypes](../../../jackson-datatypes-misc) 2 | repository as of Jackson 2.11.0 3 | 4 | Versions prior to 2.11.x will still be built from maintenance branches in this repo, however. 5 | -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4.0.0 4 | 5 | com.fasterxml.jackson 6 | jackson-base 7 | 3.0.0-SNAPSHOT 8 | 9 | com.fasterxml.jackson.datatype 10 | jackson-datatype-jsr353 11 | Jackson datatype: JSR-353 12 | 3.0.0-SNAPSHOT 13 | bundle 14 | Add-on module for Jackson (http://github.com/FasterXML/jackson) to support 15 | working with JSR-353 (JSON-P) node types via data-binding 16 | https://github.com/FasterXML/jackson-datatype-jsr353 17 | 18 | scm:git:git@github.com:FasterXML/jackson-datatype-jsr353.git 19 | scm:git:git@github.com:FasterXML/jackson-datatype-jsr353.git 20 | http://github.com/FasterXML/jackson-datatype-jsr353 21 | HEAD 22 | 23 | 24 | 25 | com/fasterxml/jackson/datatype/jsr353 26 | ${project.groupId}.jsr353 27 | 28 | 29 | 30 | 31 | 32 | com.fasterxml.jackson.core 33 | jackson-core 34 | 35 | 36 | com.fasterxml.jackson.core 37 | jackson-databind 38 | 39 | 40 | 41 | 42 | javax.json 43 | javax.json-api 44 | 1.1.4 45 | 46 | 47 | 48 | 49 | org.glassfish 50 | javax.json 51 | 1.1.4 52 | test 53 | 54 | 55 | 56 | 58 | 59 | 60 | sonatype-nexus-snapshots 61 | Sonatype Nexus Snapshots 62 | https://oss.sonatype.org/content/repositories/snapshots 63 | false 64 | true 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | com.google.code.maven-replacer-plugin 73 | replacer 74 | 75 | 78 | 79 | org.moditect 80 | moditect-maven-plugin 81 | 82 | 83 | 84 | 85 | 86 | -------------------------------------------------------------------------------- /release-notes/CREDITS-2.x: -------------------------------------------------------------------------------- 1 | Here are people who have contributed to the development of Jackson JSON processor 2 | JSR-353 (JSON-P) datatype component, version 2.x 3 | (version numbers in brackets indicate release in which the problem was fixed) 4 | 5 | Tatu Saloranta, tatu.saloranta@iki.fi: author 6 | 7 | Michał Regulski (regulskimichal@github) 8 | 9 | #13: Support for `JsonPatch` and `JsonMergePatch` defined in JSON-P 1.1 10 | w(contributed by Michał R) 11 | 12 | -------------------------------------------------------------------------------- /release-notes/VERSION-2.x: -------------------------------------------------------------------------------- 1 | Project: jackson-datatype-jsr353 2 | 3 | ------------------------------------------------------------------------ 4 | === Releases === 5 | ------------------------------------------------------------------------ 6 | 7 | 2.11.0 (not yet released) 8 | 9 | #13: Support for `JsonPatch` and `JsonMergePatch` defined in JSON-P 1.1 10 | (contributed by Michał R) 11 | - Update `javax.json` dependencies from 1.0 to 1.1.4 12 | 13 | 2.10.4 (03-May-2020) 14 | 15 | #16: Null being deserialized as null literal instead of JsonValue.NULL 16 | (reported, fix contributed by Marcos P) 17 | 18 | 2.10.3 (03-Mar-2020) 19 | 2.10.2 (05-Jan-2020) 20 | 2.10.1 (09-Nov-2019) 21 | 22 | No changes since 2.10.0 23 | 24 | 2.10.0 (26-Sep-2019) 25 | 26 | * Add JDK9 `module-info.class` using Moditect plugin 27 | 28 | 2.9.10 (21-Sep-2019) 29 | 2.9.9 (16-May-2019) 30 | 2.9.8 (15-Dec-2018) 31 | 2.9.7 (19-Sep-2018) 32 | 2.9.6 (12-Jun-2018) 33 | 2.9.5 (26-Mar-2018) 34 | 2.9.4 (24-Jan-2018) 35 | 2.9.3 (09-Dec-2017) 36 | 2.9.2 (14-Oct-2017) 37 | 2.9.0 (30-Jul-2017) 38 | 39 | No changes since 2.8. 40 | 41 | 2.8.11 (24-Dec-2017) 42 | 2.8.10 (24-Aug-2017) 43 | 2.8.9 (12-Jun-2017) 44 | 2.8.8 (05-Apr-2017) 45 | 2.8.7 (21-Feb-2017) 46 | 2.8.6 (12-Jan-2017) 47 | 2.8.5 (14-Nov-2016) 48 | 2.8.4 (14-Oct-2016) 49 | 2.8.3 (17-Sep-2016) 50 | 2.8.2 (30-Aug-2016) 51 | 2.8.1 (20-Jul-2016) 52 | 53 | No changes since 2.8.0. 54 | 55 | 2.8.0 (04-Jul-2016) 56 | 57 | No functional changes since 2.7 58 | 59 | 2.7.5 (11-Jun-2016) 60 | 2.7.4 (29-Apr-2016) 61 | 2.7.3 (16-Mar-2016) 62 | 2.7.2 (27-Feb-2016) 63 | 2.7.1 (02-Feb-2016) 64 | 2.7.0 (10-Jan-2016) 65 | 66 | No functional changes since 2.6 67 | 68 | 2.6.6 (05-Apr-2016) 69 | 2.6.5 (19-Jan-2016) 70 | 2.6.4 (07-Dec-2015) 71 | 2.6.3 (12-Oct-2015) 72 | 2.6.2 (15-Sep-2015) 73 | 2.6.1 (09-Aug-2015) 74 | 2.6.0 (19-Jul-2015) 75 | 76 | No functional changes since 2.5 77 | 78 | 2.5.3 (24-Apr-2015) 79 | 2.5.2 (29-Mar-2015) 80 | 2.5.1 (06-Feb-2015) 81 | 2.5.0 (01-Jan-2015) 82 | 83 | No functional changes since 2.4 84 | 85 | 2.4.5 (not released yet) 86 | 87 | #5: Support conversion of `BinaryNode`, as base64-encoded text 88 | (suggested by Gabor B, aborg0@github) 89 | 90 | 2.4.4 (24-Nov-2014) 91 | 2.4.3 (04-Oct-2014) 92 | 2.4.2 (15-Aug-2014) 93 | 2.4.1 (17-Jun-2014) 94 | 95 | No functional changes since 2.4.0. 96 | 97 | 2.4.0 (03-Jun-2013) 98 | 99 | No functional changes since 2.3. 100 | 101 | 2.3.3 (10-Apr-2014) 102 | 2.3.2 (01-Mar-2014) 103 | 2.3.1 (28-Dec-2013) 104 | 2.3.0 (14-Nov-2013) 105 | 2.2.3 (25-Aug-2013) 106 | 2.2.2 (27-May-2013) 107 | 2.2.1 (04-May-2013) 108 | 109 | No functional changes 110 | 111 | 2.2.0 (23-Apr-2013) 112 | 113 | First public release 114 | -------------------------------------------------------------------------------- /src/main/java/com/fasterxml/jackson/datatype/jsr353/JSR353Module.java: -------------------------------------------------------------------------------- 1 | package com.fasterxml.jackson.datatype.jsr353; 2 | 3 | import com.fasterxml.jackson.databind.*; 4 | import com.fasterxml.jackson.databind.jsontype.TypeDeserializer; 5 | import com.fasterxml.jackson.databind.module.SimpleDeserializers; 6 | import com.fasterxml.jackson.databind.module.SimpleModule; 7 | import com.fasterxml.jackson.databind.type.CollectionType; 8 | import com.fasterxml.jackson.databind.type.MapType; 9 | 10 | import javax.json.*; 11 | import javax.json.spi.JsonProvider; 12 | import java.util.Collections; 13 | 14 | public class JSR353Module extends SimpleModule 15 | { 16 | private static final long serialVersionUID = 1L; 17 | 18 | protected final JsonBuilderFactory _builderFactory; 19 | 20 | @SuppressWarnings("serial") 21 | public JSR353Module() { 22 | super(PackageVersion.VERSION); //ModuleVersion.instance.version()); 23 | 24 | JsonProvider jp = JsonProvider.provider(); 25 | _builderFactory = jp.createBuilderFactory(Collections.emptyMap()); 26 | final JsonValueDeserializer jsonValueDeser = new JsonValueDeserializer(_builderFactory); 27 | final JsonPatchDeserializer jsonPatchDeser = new JsonPatchDeserializer(jsonValueDeser); 28 | final JsonMergePatchDeserializer jsonMergePatchDeser = new JsonMergePatchDeserializer(jsonValueDeser); 29 | 30 | addSerializer(JsonValue.class, new JsonValueSerializer()); 31 | setDeserializers(new SimpleDeserializers() { 32 | @Override 33 | public JsonDeserializer findBeanDeserializer( 34 | JavaType type, 35 | DeserializationConfig config, 36 | BeanDescription beanDesc 37 | ) { 38 | if (JsonValue.class.isAssignableFrom(type.getRawClass())) { 39 | return jsonValueDeser; 40 | } 41 | if (JsonPatch.class.isAssignableFrom(type.getRawClass())) { 42 | return jsonPatchDeser; 43 | } 44 | if (JsonMergePatch.class.isAssignableFrom(type.getRawClass())) { 45 | return jsonMergePatchDeser; 46 | } 47 | return null; 48 | } 49 | 50 | @Override 51 | public JsonDeserializer findCollectionDeserializer( 52 | CollectionType type, 53 | DeserializationConfig config, 54 | BeanDescription beanDesc, 55 | TypeDeserializer elementTypeDeserializer, 56 | JsonDeserializer elementDeserializer 57 | ) { 58 | if (JsonArray.class.isAssignableFrom(type.getRawClass())) { 59 | return jsonValueDeser; 60 | } 61 | return null; 62 | } 63 | 64 | @Override 65 | public JsonDeserializer findMapDeserializer( 66 | MapType type, 67 | DeserializationConfig config, 68 | BeanDescription beanDesc, 69 | KeyDeserializer keyDeserializer, 70 | TypeDeserializer elementTypeDeserializer, 71 | JsonDeserializer elementDeserializer 72 | ) { 73 | if (JsonObject.class.isAssignableFrom(type.getRawClass())) { 74 | return jsonValueDeser; 75 | } 76 | return null; 77 | } 78 | 79 | @Override // since 2.11 80 | public boolean hasDeserializerFor(DeserializationConfig config, Class valueType) { 81 | return JsonValue.class.isAssignableFrom(valueType) || 82 | JsonPatch.class.isAssignableFrom(valueType) || 83 | JsonMergePatch.class.isAssignableFrom(valueType); 84 | } 85 | }); 86 | } 87 | } 88 | -------------------------------------------------------------------------------- /src/main/java/com/fasterxml/jackson/datatype/jsr353/JsonMergePatchDeserializer.java: -------------------------------------------------------------------------------- 1 | package com.fasterxml.jackson.datatype.jsr353; 2 | 3 | import com.fasterxml.jackson.core.JsonParser; 4 | import com.fasterxml.jackson.databind.DeserializationContext; 5 | import com.fasterxml.jackson.databind.deser.std.StdDeserializer; 6 | 7 | import javax.json.Json; 8 | import javax.json.JsonMergePatch; 9 | import java.io.IOException; 10 | 11 | public class JsonMergePatchDeserializer extends StdDeserializer { 12 | 13 | protected final JsonValueDeserializer jsonValueDeser; 14 | 15 | public JsonMergePatchDeserializer(JsonValueDeserializer jsonValueDeser) { 16 | super(JsonMergePatch.class); 17 | this.jsonValueDeser = jsonValueDeser; 18 | } 19 | 20 | @Override 21 | public JsonMergePatch deserialize(JsonParser p, DeserializationContext ctxt) throws IOException { 22 | return Json.createMergePatch(jsonValueDeser._deserializeObject(p, ctxt)); 23 | } 24 | 25 | } 26 | -------------------------------------------------------------------------------- /src/main/java/com/fasterxml/jackson/datatype/jsr353/JsonPatchDeserializer.java: -------------------------------------------------------------------------------- 1 | package com.fasterxml.jackson.datatype.jsr353; 2 | 3 | import com.fasterxml.jackson.core.JsonParser; 4 | import com.fasterxml.jackson.databind.DeserializationContext; 5 | import com.fasterxml.jackson.databind.deser.std.StdDeserializer; 6 | 7 | import javax.json.Json; 8 | import javax.json.JsonPatch; 9 | import java.io.IOException; 10 | 11 | public class JsonPatchDeserializer extends StdDeserializer { 12 | 13 | protected final JsonValueDeserializer jsonValueDeser; 14 | 15 | public JsonPatchDeserializer(JsonValueDeserializer jsonValueDeser) { 16 | super(JsonPatch.class); 17 | this.jsonValueDeser = jsonValueDeser; 18 | } 19 | 20 | @Override 21 | public JsonPatch deserialize(JsonParser p, DeserializationContext ctxt) throws IOException { 22 | return Json.createPatch(jsonValueDeser._deserializeArray(p, ctxt)); 23 | } 24 | 25 | } 26 | -------------------------------------------------------------------------------- /src/main/java/com/fasterxml/jackson/datatype/jsr353/JsonValueDeserializer.java: -------------------------------------------------------------------------------- 1 | package com.fasterxml.jackson.datatype.jsr353; 2 | 3 | import java.io.IOException; 4 | 5 | import javax.json.*; 6 | 7 | import com.fasterxml.jackson.core.JsonParser; 8 | import com.fasterxml.jackson.core.JsonParser.NumberType; 9 | import com.fasterxml.jackson.core.JsonToken; 10 | import com.fasterxml.jackson.databind.DeserializationContext; 11 | import com.fasterxml.jackson.databind.deser.std.StdDeserializer; 12 | import com.fasterxml.jackson.databind.jsontype.TypeDeserializer; 13 | 14 | public class JsonValueDeserializer extends StdDeserializer 15 | { 16 | protected final JsonBuilderFactory _builderFactory; 17 | 18 | public JsonValueDeserializer(JsonBuilderFactory bf) { 19 | super(JsonValue.class); 20 | _builderFactory = bf; 21 | } 22 | 23 | @Override 24 | public JsonValue deserialize(JsonParser p, DeserializationContext ctxt) 25 | throws IOException 26 | { 27 | switch (p.currentToken()) { 28 | case START_OBJECT: 29 | return _deserializeObject(p, ctxt); 30 | case START_ARRAY: 31 | return _deserializeArray(p, ctxt); 32 | default: 33 | return _deserializeScalar(p, ctxt); 34 | } 35 | } 36 | 37 | @Override 38 | public Object getNullValue(final DeserializationContext ctxt){ 39 | return JsonValue.NULL; 40 | } 41 | 42 | @Override 43 | public Object deserializeWithType(JsonParser p, 44 | DeserializationContext ctxt, TypeDeserializer typeDeser) 45 | throws IOException 46 | { 47 | // we will always serialize using wrapper-array; approximated by claiming it's scalar 48 | return typeDeser.deserializeTypedFromScalar(p, ctxt); 49 | } 50 | 51 | /* 52 | /********************************************************** 53 | /* Helper methods 54 | /********************************************************** 55 | */ 56 | 57 | protected JsonObject _deserializeObject(JsonParser p, DeserializationContext ctxt) 58 | throws IOException 59 | { 60 | JsonObjectBuilder b = _builderFactory.createObjectBuilder(); 61 | while (p.nextToken() != JsonToken.END_OBJECT) { 62 | String name = p.currentName(); 63 | JsonToken t = p.nextToken(); 64 | switch (t) { 65 | case START_ARRAY: 66 | b.add(name, _deserializeArray(p, ctxt)); 67 | break; 68 | case START_OBJECT: 69 | b.add(name, _deserializeObject(p, ctxt)); 70 | break; 71 | case VALUE_FALSE: 72 | b.add(name, false); 73 | break; 74 | case VALUE_TRUE: 75 | b.add(name, true); 76 | break; 77 | case VALUE_NULL: 78 | b.addNull(name); 79 | break; 80 | case VALUE_NUMBER_FLOAT: 81 | if (p.getNumberType() == NumberType.BIG_DECIMAL) { 82 | b.add(name, p.getDecimalValue()); 83 | } else { 84 | b.add(name, p.getDoubleValue()); 85 | } 86 | break; 87 | case VALUE_NUMBER_INT: 88 | // very cumbersome... but has to be done 89 | switch (p.getNumberType()) { 90 | case LONG: 91 | b.add(name, p.getLongValue()); 92 | break; 93 | case INT: 94 | b.add(name, p.getIntValue()); 95 | break; 96 | default: 97 | b.add(name, p.getBigIntegerValue()); 98 | } 99 | break; 100 | case VALUE_STRING: 101 | b.add(name, p.getText()); 102 | break; 103 | case VALUE_EMBEDDED_OBJECT: { 104 | // 26-Nov-2014, tatu: As per [issue#5], should be able to support 105 | // binary data as Base64 embedded text 106 | Object ob = p.getEmbeddedObject(); 107 | if (ob instanceof byte[]) { 108 | String b64 = ctxt.getBase64Variant().encode((byte[]) ob, false); 109 | b.add(name, b64); 110 | break; 111 | } 112 | } 113 | default: 114 | return (JsonObject) ctxt.handleUnexpectedToken(getValueType(ctxt), p); 115 | } 116 | } 117 | return b.build(); 118 | } 119 | 120 | protected JsonArray _deserializeArray(JsonParser p, DeserializationContext ctxt) 121 | throws IOException 122 | { 123 | JsonArrayBuilder b = _builderFactory.createArrayBuilder(); 124 | JsonToken t; 125 | while ((t = p.nextToken()) != JsonToken.END_ARRAY) { 126 | switch (t) { 127 | case START_ARRAY: 128 | b.add(_deserializeArray(p, ctxt)); 129 | break; 130 | case START_OBJECT: 131 | b.add(_deserializeObject(p, ctxt)); 132 | break; 133 | case VALUE_FALSE: 134 | b.add(false); 135 | break; 136 | case VALUE_TRUE: 137 | b.add(true); 138 | break; 139 | case VALUE_NULL: 140 | b.addNull(); 141 | break; 142 | case VALUE_NUMBER_FLOAT: 143 | if (p.getNumberType() == NumberType.BIG_DECIMAL) { 144 | b.add(p.getDecimalValue()); 145 | } else { 146 | b.add(p.getDoubleValue()); 147 | } 148 | break; 149 | case VALUE_NUMBER_INT: 150 | // very cumbersome... but has to be done 151 | switch (p.getNumberType()) { 152 | case LONG: 153 | b.add(p.getLongValue()); 154 | break; 155 | case INT: 156 | b.add(p.getIntValue()); 157 | break; 158 | default: 159 | b.add(p.getBigIntegerValue()); 160 | } 161 | break; 162 | case VALUE_STRING: 163 | b.add(p.getText()); 164 | break; 165 | default: 166 | return (JsonArray) ctxt.handleUnexpectedToken(getValueType(ctxt), p); 167 | } 168 | } 169 | return b.build(); 170 | } 171 | 172 | protected JsonValue _deserializeScalar(JsonParser p, DeserializationContext ctxt) 173 | throws IOException 174 | { 175 | switch (p.currentToken()) { 176 | case VALUE_EMBEDDED_OBJECT: 177 | // Not sure what to do with it -- could convert byte[] into Base64 encoded 178 | // if we wanted to... ? 179 | return (JsonValue) ctxt.handleUnexpectedToken(getValueType(ctxt), p); 180 | case VALUE_FALSE: 181 | return JsonValue.FALSE; 182 | case VALUE_TRUE: 183 | return JsonValue.TRUE; 184 | case VALUE_NULL: 185 | return JsonValue.NULL; 186 | case VALUE_NUMBER_FLOAT: 187 | // very cumbersome... but has to be done 188 | { 189 | JsonArrayBuilder b = _builderFactory.createArrayBuilder(); 190 | if (p.getNumberType() == NumberType.BIG_DECIMAL) { 191 | return b.add(p.getDecimalValue()).build().get(0); 192 | } 193 | return b.add(p.getDoubleValue()).build().get(0); 194 | } 195 | case VALUE_NUMBER_INT: 196 | // very cumbersome... but has to be done 197 | { 198 | JsonArrayBuilder b = _builderFactory.createArrayBuilder(); 199 | switch (p.getNumberType()) { 200 | case LONG: 201 | return b.add(p.getLongValue()).build().get(0); 202 | case INT: 203 | return b.add(p.getIntValue()).build().get(0); 204 | default: 205 | return b.add(p.getBigIntegerValue()).build().get(0); 206 | } 207 | } 208 | case VALUE_STRING: 209 | return _builderFactory.createArrayBuilder().add(p.getText()).build().get(0); 210 | default: // errors, should never get here 211 | // case END_ARRAY: 212 | // case END_OBJECT: 213 | // case FIELD_NAME: 214 | // case NOT_AVAILABLE: 215 | // case START_ARRAY: 216 | // case START_OBJECT: 217 | return (JsonValue) ctxt.handleUnexpectedToken(getValueType(ctxt), p); 218 | } 219 | } 220 | } 221 | -------------------------------------------------------------------------------- /src/main/java/com/fasterxml/jackson/datatype/jsr353/JsonValueSerializer.java: -------------------------------------------------------------------------------- 1 | package com.fasterxml.jackson.datatype.jsr353; 2 | 3 | import java.io.IOException; 4 | import java.util.Map; 5 | 6 | import javax.json.*; 7 | 8 | import com.fasterxml.jackson.core.*; 9 | import com.fasterxml.jackson.core.type.WritableTypeId; 10 | import com.fasterxml.jackson.databind.SerializerProvider; 11 | import com.fasterxml.jackson.databind.jsontype.TypeSerializer; 12 | import com.fasterxml.jackson.databind.ser.std.StdSerializer; 13 | 14 | public class JsonValueSerializer extends StdSerializer 15 | { 16 | public JsonValueSerializer() { 17 | super(JsonValue.class); 18 | } 19 | 20 | /* 21 | /********************************************************** 22 | /* Public API 23 | /********************************************************** 24 | */ 25 | 26 | @Override 27 | public void serialize(JsonValue value, JsonGenerator g, SerializerProvider provider) 28 | throws IOException 29 | { 30 | switch (value.getValueType()) { 31 | case ARRAY: 32 | g.writeStartArray(); 33 | serializeArrayContents((JsonArray) value, g, provider); 34 | g.writeEndArray(); 35 | break; 36 | case OBJECT: 37 | g.writeStartObject(value); 38 | serializeObjectContents((JsonObject) value, g, provider); 39 | g.writeEndObject(); 40 | break; 41 | default: // value type of some kind (scalar) 42 | serializeScalar(value, g, provider); 43 | } 44 | } 45 | 46 | @Override 47 | public void serializeWithType(JsonValue value, JsonGenerator g, SerializerProvider ctxt, 48 | TypeSerializer typeSer) 49 | throws IOException 50 | { 51 | g.setCurrentValue(value); 52 | // 25-Jul-2017, tatu: This may look wrong, but since we don't really know impl 53 | // classes we need to demote type to generic one, first: and as importantly, 54 | // need to claim that we don't really know shape to use (since that can vary 55 | // a lot). Safest way (and backwards compatible) is to claim it's scalar... 56 | // Not fully correct, but has to work for now. 57 | WritableTypeId typeIdDef = typeSer.writeTypePrefix(g, ctxt, 58 | typeSer.typeId(value, JsonValue.class, JsonToken.VALUE_EMBEDDED_OBJECT)); 59 | 60 | serialize(value, g, ctxt); 61 | typeSer.writeTypeSuffix(g, ctxt, typeIdDef); 62 | } 63 | 64 | /* 65 | /********************************************************** 66 | /* Internal methods 67 | /********************************************************** 68 | */ 69 | 70 | protected void serializeScalar(JsonValue value, 71 | JsonGenerator g, SerializerProvider provider) 72 | throws IOException 73 | { 74 | switch (value.getValueType()) { 75 | case FALSE: 76 | g.writeBoolean(false); 77 | break; 78 | case NULL: // hmmh. explicit nulls... I guess we may get them 79 | g.writeNull(); 80 | break; 81 | case NUMBER: 82 | { 83 | JsonNumber num = (JsonNumber) value; 84 | if (num.isIntegral()) { 85 | g.writeNumber(num.longValue()); 86 | } else { 87 | // 26-Feb-2013, tatu: Apparently no way to know if we need heavy BigDecimal 88 | // or not. Let's err on side of correct-if-slow to avoid losing precision. 89 | g.writeNumber(num.bigDecimalValue()); 90 | } 91 | } 92 | break; 93 | case STRING: 94 | g.writeString(((JsonString) value).getString()); 95 | break; 96 | case TRUE: 97 | g.writeBoolean(true); 98 | break; 99 | default: 100 | break; 101 | // default: // should never happen as array, object should not be called 102 | // throw new IllegalStateException("Unrecognized scalar JsonValue type: "+value.getClass().getName(); 103 | } 104 | } 105 | 106 | protected void serializeArrayContents(JsonArray values, 107 | JsonGenerator g, SerializerProvider provider) 108 | throws IOException 109 | { 110 | if (!values.isEmpty()) { 111 | for (JsonValue value : values) { 112 | serialize(value, g, provider); 113 | } 114 | } 115 | } 116 | 117 | protected void serializeObjectContents(JsonObject ob, 118 | JsonGenerator g, SerializerProvider provider) 119 | throws IOException 120 | { 121 | if (!ob.isEmpty()) { 122 | for (Map.Entry entry : ob.entrySet()) { 123 | g.writeFieldName(entry.getKey()); 124 | serialize(entry.getValue(), g, provider); 125 | } 126 | } 127 | } 128 | } 129 | -------------------------------------------------------------------------------- /src/main/java/com/fasterxml/jackson/datatype/jsr353/PackageVersion.java.in: -------------------------------------------------------------------------------- 1 | package @package@; 2 | 3 | import com.fasterxml.jackson.core.Version; 4 | import com.fasterxml.jackson.core.Versioned; 5 | import com.fasterxml.jackson.core.util.VersionUtil; 6 | 7 | /** 8 | * Automatically generated from PackageVersion.java.in during 9 | * packageVersion-generate execution of maven-replacer-plugin in 10 | * pom.xml. 11 | */ 12 | public final class PackageVersion implements Versioned { 13 | public final static Version VERSION = VersionUtil.parseVersion( 14 | "@projectversion@", "@projectgroupid@", "@projectartifactid@"); 15 | 16 | @Override 17 | public Version version() { 18 | return VERSION; 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/main/resources/LICENSE: -------------------------------------------------------------------------------- 1 | This copy of Jackson JSON processor streaming parser/generator is licensed under the 2 | Apache (Software) License, version 2.0 ("the License"). 3 | See the License for details about distribution rights, and the 4 | specific rights regarding derivate works. 5 | 6 | You may obtain a copy of the License at: 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | -------------------------------------------------------------------------------- /src/main/resources/META-INF/services/com.fasterxml.jackson.databind.Module: -------------------------------------------------------------------------------- 1 | com.fasterxml.jackson.datatype.jsr353.JSR353Module 2 | -------------------------------------------------------------------------------- /src/moditect/module-info.java: -------------------------------------------------------------------------------- 1 | // Generated 28-Mar-2019 using Moditect maven plugin 2 | module com.fasterxml.jackson.datatype.jsr353 { 3 | requires com.fasterxml.jackson.core; 4 | requires com.fasterxml.jackson.databind; 5 | 6 | requires javax.json.api; 7 | 8 | exports com.fasterxml.jackson.datatype.jsr353; 9 | 10 | provides com.fasterxml.jackson.databind.Module with 11 | com.fasterxml.jackson.datatype.jsr353.JSR353Module; 12 | } 13 | -------------------------------------------------------------------------------- /src/test/java/com/fasterxml/jackson/datatype/jsr353/JsonMergePatchDeserializationTest.java: -------------------------------------------------------------------------------- 1 | package com.fasterxml.jackson.datatype.jsr353; 2 | 3 | import com.fasterxml.jackson.databind.ObjectMapper; 4 | 5 | import javax.json.JsonMergePatch; 6 | import javax.json.JsonObject; 7 | import javax.json.JsonString; 8 | import javax.json.JsonValue; 9 | import java.util.Objects; 10 | 11 | import static org.hamcrest.CoreMatchers.instanceOf; 12 | import static org.hamcrest.CoreMatchers.is; 13 | import static org.hamcrest.MatcherAssert.assertThat; 14 | 15 | public class JsonMergePatchDeserializationTest extends TestBase { 16 | 17 | private static final ObjectMapper MAPPER = newMapper(); 18 | 19 | public void testDeserializationAndPatching() throws Exception { 20 | final String json = "{" + 21 | "\"name\":\"Json\"" + 22 | "}"; 23 | 24 | final JsonMergePatch jsonMergePatch = MAPPER.readValue(json, JsonMergePatch.class); 25 | final JsonValue jsonPatchAsJsonValue = jsonMergePatch.toJsonValue(); 26 | assertThat(jsonPatchAsJsonValue, instanceOf(JsonObject.class)); 27 | 28 | final JsonObject jsonPatchAsJsonObject = jsonPatchAsJsonValue.asJsonObject(); 29 | assertTrue(jsonPatchAsJsonObject.containsKey("name")); 30 | assertThat(jsonPatchAsJsonObject.get("name"), instanceOf(JsonString.class)); 31 | assertThat(jsonPatchAsJsonObject.getString("name"), is("Json")); 32 | 33 | assertThat(serializeAsString(jsonPatchAsJsonValue), is(json)); 34 | 35 | final Person person = new Person("John", "Smith"); 36 | final JsonValue personJson = MAPPER.convertValue(person, JsonValue.class); 37 | final JsonValue patchedPersonJson = jsonMergePatch.apply(personJson); 38 | final Person patchedPerson = MAPPER.convertValue(patchedPersonJson, Person.class); 39 | assertThat(patchedPerson, is(new Person("Json", "Smith"))); 40 | } 41 | 42 | static class Person { 43 | private String name; 44 | private String lastName; 45 | 46 | public Person() { 47 | } 48 | 49 | public Person(String name, String lastName) { 50 | this.name = name; 51 | this.lastName = lastName; 52 | } 53 | 54 | public String getName() { 55 | return name; 56 | } 57 | 58 | public void setName(String name) { 59 | this.name = name; 60 | } 61 | 62 | public String getLastName() { 63 | return lastName; 64 | } 65 | 66 | public void setLastName(String lastName) { 67 | this.lastName = lastName; 68 | } 69 | 70 | @Override 71 | public int hashCode() { 72 | return Objects.hash(name, lastName); 73 | } 74 | 75 | @Override 76 | public boolean equals(Object o) { 77 | if (this == o) return true; 78 | if (o == null || getClass() != o.getClass()) return false; 79 | Person person = (Person) o; 80 | return Objects.equals(name, person.name) && 81 | Objects.equals(lastName, person.lastName); 82 | } 83 | 84 | @Override 85 | public String toString() { 86 | return "Person{" + 87 | "name='" + name + '\'' + 88 | ", lastName='" + lastName + '\'' + 89 | '}'; 90 | } 91 | } 92 | 93 | } 94 | -------------------------------------------------------------------------------- /src/test/java/com/fasterxml/jackson/datatype/jsr353/JsonPatchDeserializationTest.java: -------------------------------------------------------------------------------- 1 | package com.fasterxml.jackson.datatype.jsr353; 2 | 3 | import com.fasterxml.jackson.databind.ObjectMapper; 4 | 5 | import javax.json.*; 6 | 7 | import java.util.Objects; 8 | 9 | import static org.hamcrest.CoreMatchers.instanceOf; 10 | import static org.hamcrest.CoreMatchers.is; 11 | import static org.hamcrest.MatcherAssert.assertThat; 12 | 13 | public class JsonPatchDeserializationTest extends TestBase { 14 | 15 | private static final ObjectMapper MAPPER = newMapper(); 16 | 17 | public void testDeserializationAndPatching() throws Exception { 18 | final String json = "[" + 19 | "{" + 20 | "\"op\":\"replace\"," + 21 | "\"path\":\"/name\"," + 22 | "\"value\":\"Json\"" + 23 | "}" + 24 | "]"; 25 | 26 | final JsonPatch jsonPatch = MAPPER.readValue(json, JsonPatch.class); 27 | final JsonArray jsonPatchAsJsonArray = jsonPatch.toJsonArray(); 28 | assertThat(jsonPatchAsJsonArray.get(0), instanceOf(JsonObject.class)); 29 | 30 | final JsonObject firstOperation = jsonPatchAsJsonArray.get(0).asJsonObject(); 31 | assertTrue(firstOperation.containsKey("op")); 32 | assertThat(firstOperation.get("op"), instanceOf(JsonString.class)); 33 | assertThat(firstOperation.getString("op"), is("replace")); 34 | 35 | assertTrue(firstOperation.containsKey("path")); 36 | assertThat(firstOperation.get("path"), instanceOf(JsonString.class)); 37 | assertThat(firstOperation.getString("path"), is("/name")); 38 | 39 | assertTrue(firstOperation.containsKey("value")); 40 | assertThat(firstOperation.get("value"), instanceOf(JsonString.class)); 41 | assertThat(firstOperation.getString("value"), is("Json")); 42 | 43 | assertThat(serializeAsString(jsonPatchAsJsonArray), is(json)); 44 | 45 | final Person person = new Person("John", "Smith"); 46 | final JsonStructure personJson = MAPPER.convertValue(person, JsonStructure.class); 47 | final JsonStructure patchedPersonJson = jsonPatch.apply(personJson); 48 | final Person patchedPerson = MAPPER.convertValue(patchedPersonJson, Person.class); 49 | assertThat(patchedPerson, is(new Person("Json", "Smith"))); 50 | } 51 | 52 | static class Person { 53 | private String name; 54 | private String lastName; 55 | 56 | public Person() { 57 | } 58 | 59 | public Person(String name, String lastName) { 60 | this.name = name; 61 | this.lastName = lastName; 62 | } 63 | 64 | public String getName() { 65 | return name; 66 | } 67 | 68 | public void setName(String name) { 69 | this.name = name; 70 | } 71 | 72 | public String getLastName() { 73 | return lastName; 74 | } 75 | 76 | public void setLastName(String lastName) { 77 | this.lastName = lastName; 78 | } 79 | 80 | @Override 81 | public int hashCode() { 82 | return Objects.hash(name, lastName); 83 | } 84 | 85 | @Override 86 | public boolean equals(Object o) { 87 | if (this == o) return true; 88 | if (o == null || getClass() != o.getClass()) return false; 89 | Person person = (Person) o; 90 | return Objects.equals(name, person.name) && 91 | Objects.equals(lastName, person.lastName); 92 | } 93 | 94 | @Override 95 | public String toString() { 96 | return "Person{" + 97 | "name='" + name + '\'' + 98 | ", lastName='" + lastName + '\'' + 99 | '}'; 100 | } 101 | } 102 | 103 | } 104 | -------------------------------------------------------------------------------- /src/test/java/com/fasterxml/jackson/datatype/jsr353/JsonValueDeserializationTest.java: -------------------------------------------------------------------------------- 1 | package com.fasterxml.jackson.datatype.jsr353; 2 | 3 | import com.fasterxml.jackson.databind.json.JsonMapper; 4 | import javax.json.*; 5 | import javax.json.JsonValue.ValueType; 6 | 7 | import com.fasterxml.jackson.databind.ObjectMapper; 8 | import com.fasterxml.jackson.databind.node.ObjectNode; 9 | 10 | public class JsonValueDeserializationTest extends TestBase 11 | { 12 | private final ObjectMapper MAPPER = newMapper(); 13 | 14 | public void testSimpleArray() throws Exception 15 | { 16 | final String JSON = "[1,true,\"foo\"]"; 17 | JsonValue v = MAPPER.readValue(JSON, JsonValue.class); 18 | assertTrue(v instanceof JsonArray); 19 | JsonArray a = (JsonArray) v; 20 | assertEquals(3, a.size()); 21 | assertTrue(a.get(0) instanceof JsonNumber); 22 | assertSame(JsonValue.TRUE, a.get(1)); 23 | assertTrue(a.get(2) instanceof JsonString); 24 | 25 | // also, should work with explicit type 26 | JsonArray array = MAPPER.readValue(JSON, JsonArray.class); 27 | assertEquals(3, array.size()); 28 | 29 | // and round-tripping ought to be ok: 30 | assertEquals(JSON, serializeAsString(v)); 31 | } 32 | 33 | public void testNestedArray() throws Exception 34 | { 35 | final String JSON = "[1,[false,45],{\"foo\":13}]"; 36 | JsonValue v = MAPPER.readValue(JSON, JsonValue.class); 37 | assertTrue(v instanceof JsonArray); 38 | JsonArray a = (JsonArray) v; 39 | assertEquals(3, a.size()); 40 | assertTrue(a.get(0) instanceof JsonNumber); 41 | assertTrue(a.get(1) instanceof JsonArray); 42 | assertTrue(a.get(2) instanceof JsonObject); 43 | 44 | // and round-tripping ought to be ok: 45 | assertEquals(JSON, serializeAsString(v)); 46 | } 47 | 48 | public void testSimpleObject() throws Exception 49 | { 50 | final String JSON = "{\"a\":12.5,\"b\":\"Text\"}"; 51 | JsonValue v = MAPPER.readValue(JSON, JsonValue.class); 52 | assertTrue(v instanceof JsonObject); 53 | JsonObject ob = (JsonObject) v; 54 | assertEquals(2, ob.size()); 55 | 56 | assertTrue(ob.get("a") instanceof JsonNumber); 57 | assertEquals(12.5, ((JsonNumber) ob.get("a")).doubleValue()); 58 | assertTrue(ob.get("b") instanceof JsonString); 59 | assertEquals("Text", ((JsonString) ob.get("b")).getString()); 60 | 61 | // also, should work with explicit type 62 | ob = MAPPER.readValue(JSON, JsonObject.class); 63 | assertEquals(2, ob.size()); 64 | 65 | // and round-tripping ought to be ok: 66 | assertEquals(JSON, serializeAsString(v)); 67 | } 68 | 69 | public void testNestedObject() throws Exception 70 | { 71 | final String JSON = "{\"array\":[1,2],\"obj\":{\"first\":true}}"; 72 | JsonValue v = MAPPER.readValue(JSON, JsonValue.class); 73 | assertTrue(v instanceof JsonObject); 74 | JsonObject ob = (JsonObject) v; 75 | assertEquals(2, ob.size()); 76 | 77 | assertTrue(ob.get("array") instanceof JsonArray); 78 | assertEquals(2, ((JsonArray) ob.get("array")).size()); 79 | assertTrue(ob.get("obj") instanceof JsonObject); 80 | assertEquals(1, ((JsonObject) ob.get("obj")).size()); 81 | 82 | // also, should work with explicit type 83 | ob = MAPPER.readValue(JSON, JsonObject.class); 84 | assertEquals(2, ob.size()); 85 | 86 | // and round-tripping ought to be ok: 87 | assertEquals(JSON, serializeAsString(v)); 88 | } 89 | 90 | // for [datatype-jsr353#5] 91 | public void testBinaryNode() throws Exception 92 | { 93 | ObjectNode root = MAPPER.createObjectNode(); 94 | root.put("b", new byte[1]); 95 | JsonValue v = MAPPER.convertValue(root, JsonValue.class); 96 | assertNotNull(v); 97 | assertEquals(ValueType.OBJECT, v.getValueType()); 98 | JsonValue v2 = ((javax.json.JsonObject) v).get("b"); 99 | assertNotNull(v2); 100 | assertEquals(ValueType.STRING, v2.getValueType()); 101 | String str = ((JsonString) v2).getString(); 102 | assertEquals("AA==", str); // single zero byte 103 | } 104 | 105 | // for [datatype-jsr353#16] 106 | public void testNullNode() throws Exception 107 | { 108 | final String serializedNull = MAPPER.writeValueAsString(JsonValue.NULL); 109 | assertEquals("null", serializedNull); 110 | final JsonValue deserializedNull = MAPPER.readValue(serializedNull, JsonValue.class); 111 | assertEquals(JsonValue.NULL, deserializedNull); 112 | } 113 | } 114 | -------------------------------------------------------------------------------- /src/test/java/com/fasterxml/jackson/datatype/jsr353/JsonValueSerializationTest.java: -------------------------------------------------------------------------------- 1 | package com.fasterxml.jackson.datatype.jsr353; 2 | 3 | import java.math.BigDecimal; 4 | 5 | import javax.json.*; 6 | 7 | public class JsonValueSerializationTest extends TestBase 8 | { 9 | public void testSimpleArray() throws Exception 10 | { 11 | JsonArray arr = arrayBuilder() 12 | .add(true) 13 | .addNull() 14 | .add(123) 15 | .add(new BigDecimal("15.25")) 16 | .build(); 17 | assertEquals("[true,null,123,15.25]", serializeAsString(arr)); 18 | } 19 | 20 | public void testNestedArray() throws Exception 21 | { 22 | JsonArray arr = arrayBuilder() 23 | .add(1) 24 | .add(arrayBuilder().add(false).add(45).build()) 25 | .add(objectBuilder().add("foo", 13).build()) 26 | .build(); 27 | assertEquals("[1,[false,45],{\"foo\":13}]", serializeAsString(arr)); 28 | } 29 | 30 | public void testSimpleObject() throws Exception 31 | { 32 | JsonObject ob = objectBuilder() 33 | .add("a", 123) 34 | .add("b", "Text") 35 | .build(); 36 | // not sure if order is guaranteed but: 37 | assertEquals("{\"a\":123,\"b\":\"Text\"}", serializeAsString(ob)); 38 | } 39 | 40 | public void testNestedObject() throws Exception 41 | { 42 | JsonObject ob = objectBuilder() 43 | .add("array", arrayBuilder().add(1).add(2)) 44 | .add("obj", objectBuilder().add("first", true)) 45 | .build(); 46 | // not sure if order is guaranteed but: 47 | assertEquals("{\"array\":[1,2],\"obj\":{\"first\":true}}", serializeAsString(ob)); 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /src/test/java/com/fasterxml/jackson/datatype/jsr353/PolymorphicTest.java: -------------------------------------------------------------------------------- 1 | package com.fasterxml.jackson.datatype.jsr353; 2 | 3 | import javax.json.*; 4 | 5 | import com.fasterxml.jackson.annotation.JsonTypeInfo; 6 | import com.fasterxml.jackson.databind.ObjectMapper; 7 | 8 | public class PolymorphicTest extends TestBase 9 | { 10 | static class Wrapper { 11 | @JsonTypeInfo(include = JsonTypeInfo.As.PROPERTY, use = JsonTypeInfo.Id.CLASS) 12 | public Object w; 13 | 14 | public Wrapper() { } 15 | public Wrapper(Object o) { w = o; } 16 | } 17 | 18 | private final ObjectMapper MAPPER = mapperBuilder() 19 | .polymorphicTypeValidator(new NoCheckSubTypeValidator()) 20 | .build(); 21 | 22 | public void testObjectAsTyped() throws Exception 23 | { 24 | final String INPUT = "{\"array\":[1,2],\"obj\":{\"first\":true}}"; 25 | JsonValue v = MAPPER.readValue(INPUT, JsonValue.class); 26 | 27 | String POLY_JSON = MAPPER.writeValueAsString(new Wrapper(v)); 28 | 29 | Wrapper w = MAPPER.readValue(POLY_JSON, Wrapper.class); 30 | assertTrue(w.w instanceof JsonObject); 31 | JsonObject ob = (JsonObject) w.w; 32 | assertEquals(2, ob.size()); 33 | JsonArray arr = ob.getJsonArray("array"); 34 | assertNotNull(arr); 35 | assertEquals(2, arr.size()); 36 | assertEquals(1, arr.getInt(0)); 37 | } 38 | 39 | public void testArrayAsTyped() throws Exception 40 | { 41 | final String INPUT = "[1,{\"a\":true}]"; 42 | JsonValue v = MAPPER.readValue(INPUT, JsonValue.class); 43 | 44 | String POLY_JSON = MAPPER.writeValueAsString(new Wrapper(v)); 45 | 46 | Wrapper w = MAPPER.readValue(POLY_JSON, Wrapper.class); 47 | assertTrue(w.w instanceof JsonArray); 48 | JsonArray arr = (JsonArray) w.w; 49 | assertEquals(2, arr.size()); 50 | JsonObject ob = arr.getJsonObject(1); 51 | assertNotNull(arr); 52 | assertEquals(1, ob.size()); 53 | assertEquals(JsonValue.TRUE, ob.get("a")); 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /src/test/java/com/fasterxml/jackson/datatype/jsr353/TestBase.java: -------------------------------------------------------------------------------- 1 | package com.fasterxml.jackson.datatype.jsr353; 2 | 3 | import java.io.IOException; 4 | 5 | import javax.json.*; 6 | 7 | import com.fasterxml.jackson.databind.*; 8 | import com.fasterxml.jackson.databind.json.JsonMapper; 9 | import com.fasterxml.jackson.databind.jsontype.PolymorphicTypeValidator; 10 | 11 | public abstract class TestBase extends junit.framework.TestCase 12 | { 13 | static class NoCheckSubTypeValidator 14 | extends PolymorphicTypeValidator.Base 15 | { 16 | private static final long serialVersionUID = 1L; 17 | 18 | @Override 19 | public Validity validateBaseType(DatabindContext ctxt, JavaType baseType) { 20 | return Validity.ALLOWED; 21 | } 22 | } 23 | 24 | private final static JSR353Module MODULE = new JSR353Module(); 25 | 26 | private final static ObjectMapper SHARED_MAPPER = newMapper(); 27 | 28 | protected static ObjectMapper newMapper() { 29 | return mapperBuilder().build(); 30 | } 31 | 32 | protected static JsonMapper.Builder mapperBuilder() { 33 | return JsonMapper.builder() 34 | .addModule(MODULE); 35 | } 36 | 37 | protected static ObjectMapper sharedMapper() { 38 | return SHARED_MAPPER; 39 | } 40 | 41 | protected String serializeAsString(JsonValue node) throws IOException { 42 | return SHARED_MAPPER.writeValueAsString(node); 43 | } 44 | 45 | protected JsonArrayBuilder arrayBuilder() { 46 | return MODULE._builderFactory.createArrayBuilder(); 47 | } 48 | 49 | protected JsonObjectBuilder objectBuilder() { 50 | return MODULE._builderFactory.createObjectBuilder(); 51 | } 52 | } 53 | --------------------------------------------------------------------------------