From bef1dbc990c3cd41769194a234f9e5ab88923b13 Mon Sep 17 00:00:00 2001 From: Bidyadhar Mohanty Date: Fri, 20 Dec 2024 18:13:48 +0530 Subject: [PATCH] Added javadoc. Cleanup. Modified README.md and addressed comments. --- ojdbc-provider-jackson-oson/README.md | 23 +- .../provider/oson/JacksonOsonConverter.java | 1 - .../jdbc/provider/oson/OsonFactory.java | 5 +- .../jdbc/provider/oson/OsonGenerator.java | 37 ++- .../oracle/jdbc/provider/oson/OsonModule.java | 28 +- .../oracle/jdbc/provider/oson/OsonParser.java | 157 ++++++----- .../java/oracle/jdbc/provider/oson/Util.java | 134 +++++++--- .../deser/OsonBigIntegerDeserializer.java | 1 - .../oson/deser/OsonBooleanDeserializer.java | 82 ++++-- .../oson/deser/OsonByteDeserializer.java | 38 +-- .../deser/OsonConverterArrayDeserializer.java | 98 ++++--- .../oson/deser/OsonConverterDeserializer.java | 248 ++++++++++------- .../oson/deser/OsonDateDeserializer.java | 249 +++++++++--------- .../oson/deser/OsonLocalDateDeserializer.java | 178 ++++++++++--- .../deser/OsonLocalDateTimeDeserializer.java | 1 - .../deser/OsonSerializableDeserializer.java | 90 +++++++ .../oson/deser/OsonSqlDateDeserializer.java | 84 ++++-- .../oson/deser/OsonTimeStampDeserializer.java | 104 ++++++-- .../oson/deser/OsonUUIDDeserializer.java | 66 +++-- .../deser/OsosnSerializableDeserializer.java | 30 --- .../ser/OsonConverterArraySerializer.java | 150 ++++++----- ...iser.java => OsonConverterSerializer.java} | 63 +++-- .../provider/oson/ser/OsonDateSerializer.java | 177 +++++++------ .../provider/oson/ser/OsonEnumSerializer.java | 116 +++++--- .../oson/ser/OsonLocalDateSerializer.java | 136 ++++++++-- .../oson/ser/OsonLocalDateTimeSerializer.java | 4 + .../oson/ser/OsonSerializableSerializer.java | 88 +++++-- 27 files changed, 1575 insertions(+), 813 deletions(-) create mode 100644 ojdbc-provider-jackson-oson/src/main/java/oracle/jdbc/provider/oson/deser/OsonSerializableDeserializer.java delete mode 100644 ojdbc-provider-jackson-oson/src/main/java/oracle/jdbc/provider/oson/deser/OsosnSerializableDeserializer.java rename ojdbc-provider-jackson-oson/src/main/java/oracle/jdbc/provider/oson/ser/{OsonConverterSerialiser.java => OsonConverterSerializer.java} (56%) diff --git a/ojdbc-provider-jackson-oson/README.md b/ojdbc-provider-jackson-oson/README.md index 9858d903..99fcec63 100644 --- a/ojdbc-provider-jackson-oson/README.md +++ b/ojdbc-provider-jackson-oson/README.md @@ -27,8 +27,23 @@ JDK versions. The coordinates for the latest release are: ``` ### Note -The extention uses ojdbc8 as it's dependency. If the application env. has other ojdbc jar -in the dependency, be sure to exclude the ojdbc8 jar from the extention. +The extension uses ojdbc8 as it's dependency. If the application environment has a different version of JDBC, +be sure to exclude ojdbc8 from the dependencies. +It can be done in maven as: + +```xml + + com.oracle.database.jdbc + ojdbc-provider-jackson-oson + 1.0.2 + + + com.oracle.database.jdbc + ojdbc8 + + + +``` ## Building the provider module 1. Clone the repository: @@ -48,12 +63,12 @@ Usage Examples for Oracle Jackson OSON Provider Extensions can be found at [ojdb - **[AccessJsonColumnUsingPOJOAndJsonProvider](../ojdbc-provider-samples/src/main/java/oracle/jdbc/provider/oson/sample/AccessJsonColumnUsingPOJOAndJsonProvider.java)**: Demonstrates the usage of the Jackson OSON provider to serialize a Plain Old Java Object (POJO) to OSON bytes in order to save it in a JSON column in the database and deserialize OSON bytes to - POJO during retrieval. In this case, the JDBC Thin Driver invokes the provider to serialize/deserialize. + a POJO during retrieval. In this case, the JDBC Thin Driver invokes the provider to serialize/deserialize. - **[AccessJsonColumnUsingHibernate](../ojdbc-provider-samples/src/main/java/oracle/jdbc/provider/oson/sample/AccessJsonColumnUsingHibernate.java)**: Performs the same task as above using Hibernate. - **[AccessJsonColumnUsingJacksonObjectNode](../ojdbc-provider-samples/src/main/java/oracle/jdbc/provider/oson/sample/AccessJsonColumnUsingJacksonObjectNode.java)**: Demonstrates the usage of the Jackson OSON provider to serialize Jackson's ObjectNode - to OSON bytes for insertion and vice-versa for retrieval. + to OSON bytes for insertion and retrieval. - **[AccessJsonColumnUsingPOJO](../ojdbc-provider-samples/src/main/java/oracle/jdbc/provider/oson/sample/AccessJsonColumnUsingPOJO.java)**: Demonstrates how to use the Jackson OSON provider APIs to Serialize/Deserialize POJO and use JDBC to directly insert the OSON bytes. diff --git a/ojdbc-provider-jackson-oson/src/main/java/oracle/jdbc/provider/oson/JacksonOsonConverter.java b/ojdbc-provider-jackson-oson/src/main/java/oracle/jdbc/provider/oson/JacksonOsonConverter.java index eda557a3..efb85d50 100644 --- a/ojdbc-provider-jackson-oson/src/main/java/oracle/jdbc/provider/oson/JacksonOsonConverter.java +++ b/ojdbc-provider-jackson-oson/src/main/java/oracle/jdbc/provider/oson/JacksonOsonConverter.java @@ -73,7 +73,6 @@ public class JacksonOsonConverter implements OsonConverter{ om.findAndRegisterModules(); om.registerModule(new OsonModule()); om.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS); -// om.setAnnotationIntrospector(new AnnotationIntrospector()); } /** diff --git a/ojdbc-provider-jackson-oson/src/main/java/oracle/jdbc/provider/oson/OsonFactory.java b/ojdbc-provider-jackson-oson/src/main/java/oracle/jdbc/provider/oson/OsonFactory.java index 1b0824be..02ee875b 100644 --- a/ojdbc-provider-jackson-oson/src/main/java/oracle/jdbc/provider/oson/OsonFactory.java +++ b/ojdbc-provider-jackson-oson/src/main/java/oracle/jdbc/provider/oson/OsonFactory.java @@ -396,11 +396,10 @@ public JsonParser createParser(OracleJsonParser oParser) { * @param input DataInput to use for reading content to parse * @param ctxt I/O context to use for parsing * - * @return - * @throws IOException + * @return the JsonParser from DataInput */ @Override - protected JsonParser _createParser(DataInput input, IOContext ctxt) throws IOException { + protected JsonParser _createParser(DataInput input, IOContext ctxt) { InputStream stream = new InputStream() { @Override diff --git a/ojdbc-provider-jackson-oson/src/main/java/oracle/jdbc/provider/oson/OsonGenerator.java b/ojdbc-provider-jackson-oson/src/main/java/oracle/jdbc/provider/oson/OsonGenerator.java index 67672337..a8d86e60 100644 --- a/ojdbc-provider-jackson-oson/src/main/java/oracle/jdbc/provider/oson/OsonGenerator.java +++ b/ojdbc-provider-jackson-oson/src/main/java/oracle/jdbc/provider/oson/OsonGenerator.java @@ -75,7 +75,7 @@ public class OsonGenerator extends GeneratorBase { private static final boolean DEBUG = false; - private Logger logger = Logger.getLogger("OsonLogger"); + private Logger logger = Logger.getLogger(OsonGenerator.class.getName()); private OutputStream out = null; private OracleJsonGenerator gen = null; @@ -126,7 +126,7 @@ public boolean canWriteBinaryNatively() { */ @Override public void flush() throws IOException { - if(DEBUG) logger.log(Level.FINEST, "flush"); + logger.log(Level.FINEST, "flush"); gen.flush(); } @@ -135,7 +135,7 @@ public void flush() throws IOException { */ @Override protected void _releaseBuffers() { - if(DEBUG) logger.log(Level.FINEST, "_releaseBuffers"); + logger.log(Level.FINEST, "_releaseBuffers"); if(out instanceof ByteArrayOutputStream) ((ByteArrayOutputStream) out).reset(); } @@ -519,11 +519,18 @@ public boolean isClosed() { */ @Override public void close() throws IOException { - if(DEBUG) logger.log(Level.FINEST, "close"); + logger.log(Level.FINEST, "close"); gen.close(); closed = true; } + /** + * Writes a `java.util.Date` object as an Oracle JSON `DATE`. + * If the input is a `java.sql.Date`, it is first converted to a `DATE` object. + * + * @param value the `Date` to write + * @throws IOException if an I/O error occurs during writing + */ public void writeDate(Date value) throws IOException { _verifyValueWrite("write date"); @@ -539,18 +546,32 @@ public void writeDate(Date value) throws IOException { } } + /** + * Writes a `LocalDate` object as an Oracle JSON `DATE`. + * Converts the `LocalDate` to a `DATE` object and serializes it. + * + * @param value the `LocalDate` to write + * @throws IOException if an I/O error occurs during writing + */ public void writeLocalDate(LocalDate value) throws IOException { _verifyValueWrite("write LocalDate"); - DATE dd = null; + try { - dd = new DATE(value); + DATE dd = new DATE(value); + OracleJsonDate jsonDate = new OracleJsonDateImpl(dd.shareBytes()); + gen.write(jsonDate); } catch (SQLException e) { throw new RuntimeException(e); } - OracleJsonDate jsonDate = new OracleJsonDateImpl(dd.shareBytes()); - gen.write(jsonDate); } + /** + * Writes a `Timestamp` object as an Oracle JSON `TIMESTAMP`. + * Converts the `Timestamp` to a `TIMESTAMP` object and serializes it. + * + * @param value the `Timestamp` to write + * @throws IOException if an I/O error occurs during writing + */ public void writeTimeStamp(Timestamp value) throws IOException { _verifyValueWrite("write TimeStamp"); TIMESTAMP timestamp = new TIMESTAMP(value); diff --git a/ojdbc-provider-jackson-oson/src/main/java/oracle/jdbc/provider/oson/OsonModule.java b/ojdbc-provider-jackson-oson/src/main/java/oracle/jdbc/provider/oson/OsonModule.java index 5fef2f32..7a4ffcb3 100644 --- a/ojdbc-provider-jackson-oson/src/main/java/oracle/jdbc/provider/oson/OsonModule.java +++ b/ojdbc-provider-jackson-oson/src/main/java/oracle/jdbc/provider/oson/OsonModule.java @@ -72,7 +72,16 @@ *
  • {@link Duration} - {@link OsonDurationDeserializer} and {@link OsonDurationSerializer}
  • *
  • {@link BigInteger} - {@link OsonBigIntegerDeserializer} and {@link OsonBigIntegerSerializer}
  • *
  • {@link Year} - {@link OsonYearDeserializer} and {@link OsonYearSerializer}
  • - *
  • {@code byte[]} - {@link OsonByteDeserializer} and {@link OsonByteSerializer}
  • + *
  • {@link byte[]} - {@link OsonByteDeserializer} and {@link OsonByteSerializer}
  • + *
  • {@link java.util.Date}- {@link OsonDateSerializer} and {@link OsonDateDeserializer}
  • + *
  • {@link java.sql.Date}- {@link OsonDateSerializer} and {@link OsonSqlDateDeserializer}
  • + *
  • {@link Timestamp}- {@link OsonDateSerializer} and {@link OsonTimeStampDeserializer}
  • + *
  • {@link LocalDate}- {@link OsonLocalDateSerializer} and {@link OsonLocalDateDeserializer}
  • + *
  • {@link Enum}- {@link OsonEnumSerializer}
  • + *
  • {@link jakarta.persistence.AttributeConverter} - {@link OsonConverterSerializer} and {@link OsonConverterDeserializer}
  • + *
  • {@code jakarta.persistence.AttributeConverter[]} - {@link OsonConverterArraySerializer} and {@link OsonConverterArrayDeserializer}
  • + *
  • {@link Boolean}- {@link OsonBooleanDeserializer}
  • + *
  • {@link UUID}- {@link OsonUUIDDeserializer}
  • * * */ @@ -81,6 +90,8 @@ public class OsonModule extends SimpleModule { public static String groupId; public static String artifactId; public static Version VERSION; + public static final String PROPERTIES_FILE_PATH = + "/META-INF/maven/com.oracle.database.jdbc/ojdbc-provider-jackson-oson/pom.properties"; static { instantiateProviderVersionInfo(); @@ -88,15 +99,14 @@ public class OsonModule extends SimpleModule { } private static void instantiateProviderVersionInfo() { - String propertiesFilePath = "/META-INF/maven/com.oracle.database.jdbc/ojdbc-provider-jackson-oson/pom.properties"; Properties properties = new Properties(); - try (InputStream inputStream = OsonModule.class.getResourceAsStream(propertiesFilePath)) { + try (InputStream inputStream = OsonModule.class.getResourceAsStream(PROPERTIES_FILE_PATH)) { if (inputStream != null) { properties.load(inputStream); providerVersion = properties.getProperty("version"); - groupId = properties.getProperty("artifactId"); - artifactId = properties.getProperty("groupId"); + groupId = properties.getProperty("groupId"); + artifactId = properties.getProperty("artifactId"); } else { // when the tests are run locally using IDE. @@ -158,7 +168,7 @@ public JsonSerializer modifySerializer(SerializationConfig config, BeanDescri while (properties.hasNext()) { boolean serializerAssigned = false; BeanPropertyWriter writer = (BeanPropertyWriter) properties.next(); - if (writer.getMember().hasAnnotation(Convert.class)) { + if (writer.getMember() != null && writer.getMember().hasAnnotation(Convert.class)) { Convert annotation = writer.getMember().getAnnotation(Convert.class); Class converterClass = annotation.converter(); serializerAssigned = true; @@ -166,7 +176,7 @@ public JsonSerializer modifySerializer(SerializationConfig config, BeanDescri JsonSerializer mySerializer = new OsonConverterArraySerializer(converterClass); writer.assignSerializer((JsonSerializer) mySerializer); } else { - JsonSerializer mySerializer = new OsonConverterSerialiser(converterClass); + JsonSerializer mySerializer = new OsonConverterSerializer(converterClass); writer.assignSerializer(mySerializer); } } @@ -194,7 +204,7 @@ public JsonDeserializer modifyDeserializer(DeserializationConfig config, Bean while (properties.hasNext()) { SettableBeanProperty property = properties.next(); boolean deserializerAssigned = false; - if (property.getMember().hasAnnotation(Convert.class)) { + if (property.getMember() != null && property.getMember().hasAnnotation(Convert.class)) { Convert annotation = property.getMember().getAnnotation(Convert.class); Class converterClass = annotation.converter(); deserializerAssigned = true; @@ -209,7 +219,7 @@ public JsonDeserializer modifyDeserializer(DeserializationConfig config, Bean if (Util.implementsSerializable(property.getType().getInterfaces()) && !Util.isJavaWrapperSerializable(property.getType()) && !deserializerAssigned){ - JsonDeserializer deser = OsosnSerializableDeserializer.INSTANCE; + JsonDeserializer deser = OsonSerializableDeserializer.INSTANCE; ((BeanDeserializer) deserializer).replaceProperty(property,property.withValueDeserializer(deser)); } } diff --git a/ojdbc-provider-jackson-oson/src/main/java/oracle/jdbc/provider/oson/OsonParser.java b/ojdbc-provider-jackson-oson/src/main/java/oracle/jdbc/provider/oson/OsonParser.java index f1a3bf35..4553a159 100644 --- a/ojdbc-provider-jackson-oson/src/main/java/oracle/jdbc/provider/oson/OsonParser.java +++ b/ojdbc-provider-jackson-oson/src/main/java/oracle/jdbc/provider/oson/OsonParser.java @@ -68,12 +68,12 @@ public class OsonParser extends ParserBase { private static final boolean DEBUG = false; /** Logger for debugging purposes. */ - private final Logger logger = Logger.getLogger("OsonLogger"); + private final Logger logger = Logger.getLogger(OsonParser.class.getName()); /** The OracleJsonParser instance to parse Oracle JSON data. */ private final OracleJsonParser parser; - /** Contains the current field name and hence makes this instance stateful. */ + /** Contains the current field name and hence makes this instance stateful.*/ private String fieldName; private OracleJsonParser.Event currentEvent; @@ -81,33 +81,51 @@ public class OsonParser extends ParserBase { private ObjectCodec _codec; - private static final Map OSON_EVENT_TO_JSON_TOKEN = new HashMap<>(); + private static final Map + OSON_EVENT_TO_JSON_TOKEN = new HashMap<>(); - /** - * A static map to map OracleJsonParser events to Jackson's JsonToken values. - */ static { - OSON_EVENT_TO_JSON_TOKEN.put(OracleJsonParser.Event.START_ARRAY, JsonToken.START_ARRAY); - OSON_EVENT_TO_JSON_TOKEN.put(OracleJsonParser.Event.END_ARRAY, JsonToken.END_ARRAY); - - OSON_EVENT_TO_JSON_TOKEN.put(OracleJsonParser.Event.START_OBJECT, JsonToken.START_OBJECT); - OSON_EVENT_TO_JSON_TOKEN.put(OracleJsonParser.Event.END_OBJECT, JsonToken.END_OBJECT); - - OSON_EVENT_TO_JSON_TOKEN.put(OracleJsonParser.Event.KEY_NAME, JsonToken.FIELD_NAME); - OSON_EVENT_TO_JSON_TOKEN.put(OracleJsonParser.Event.VALUE_STRING, JsonToken.VALUE_STRING); - OSON_EVENT_TO_JSON_TOKEN.put(OracleJsonParser.Event.VALUE_NULL, JsonToken.VALUE_NULL); - OSON_EVENT_TO_JSON_TOKEN.put(OracleJsonParser.Event.VALUE_TRUE, JsonToken.VALUE_TRUE); - OSON_EVENT_TO_JSON_TOKEN.put(OracleJsonParser.Event.VALUE_FALSE, JsonToken.VALUE_FALSE); + /** + * A static map to map OracleJsonParser events to Jackson's JsonToken values. + */ + OSON_EVENT_TO_JSON_TOKEN.put(OracleJsonParser.Event.START_ARRAY, + JsonToken.START_ARRAY); + OSON_EVENT_TO_JSON_TOKEN.put(OracleJsonParser.Event.END_ARRAY, + JsonToken.END_ARRAY); + + OSON_EVENT_TO_JSON_TOKEN.put(OracleJsonParser.Event.START_OBJECT, + JsonToken.START_OBJECT); + OSON_EVENT_TO_JSON_TOKEN.put(OracleJsonParser.Event.END_OBJECT, + JsonToken.END_OBJECT); + + OSON_EVENT_TO_JSON_TOKEN.put(OracleJsonParser.Event.KEY_NAME, + JsonToken.FIELD_NAME); + OSON_EVENT_TO_JSON_TOKEN.put(OracleJsonParser.Event.VALUE_STRING, + JsonToken.VALUE_STRING); + OSON_EVENT_TO_JSON_TOKEN.put(OracleJsonParser.Event.VALUE_NULL, + JsonToken.VALUE_NULL); + OSON_EVENT_TO_JSON_TOKEN.put(OracleJsonParser.Event.VALUE_TRUE, + JsonToken.VALUE_TRUE); + OSON_EVENT_TO_JSON_TOKEN.put(OracleJsonParser.Event.VALUE_FALSE, + JsonToken.VALUE_FALSE); // Different - OSON_EVENT_TO_JSON_TOKEN.put(OracleJsonParser.Event.VALUE_DATE, JsonToken.VALUE_STRING); - OSON_EVENT_TO_JSON_TOKEN.put(OracleJsonParser.Event.VALUE_DOUBLE, JsonToken.VALUE_NUMBER_FLOAT); - OSON_EVENT_TO_JSON_TOKEN.put(OracleJsonParser.Event.VALUE_FLOAT, JsonToken.VALUE_NUMBER_FLOAT); - OSON_EVENT_TO_JSON_TOKEN.put(OracleJsonParser.Event.VALUE_TIMESTAMP, JsonToken.VALUE_STRING); - OSON_EVENT_TO_JSON_TOKEN.put(OracleJsonParser.Event.VALUE_TIMESTAMPTZ, JsonToken.VALUE_STRING); - OSON_EVENT_TO_JSON_TOKEN.put(OracleJsonParser.Event.VALUE_BINARY,JsonToken.VALUE_EMBEDDED_OBJECT); - OSON_EVENT_TO_JSON_TOKEN.put(OracleJsonParser.Event.VALUE_INTERVALDS, JsonToken.VALUE_STRING); - OSON_EVENT_TO_JSON_TOKEN.put(OracleJsonParser.Event.VALUE_INTERVALYM, JsonToken.VALUE_STRING); + OSON_EVENT_TO_JSON_TOKEN.put(OracleJsonParser.Event.VALUE_DATE, + JsonToken.VALUE_STRING); + OSON_EVENT_TO_JSON_TOKEN.put(OracleJsonParser.Event.VALUE_DOUBLE, + JsonToken.VALUE_NUMBER_FLOAT); + OSON_EVENT_TO_JSON_TOKEN.put(OracleJsonParser.Event.VALUE_FLOAT, + JsonToken.VALUE_NUMBER_FLOAT); + OSON_EVENT_TO_JSON_TOKEN.put(OracleJsonParser.Event.VALUE_TIMESTAMP, + JsonToken.VALUE_STRING); + OSON_EVENT_TO_JSON_TOKEN.put(OracleJsonParser.Event.VALUE_TIMESTAMPTZ, + JsonToken.VALUE_STRING); + OSON_EVENT_TO_JSON_TOKEN.put(OracleJsonParser.Event.VALUE_BINARY, + JsonToken.VALUE_EMBEDDED_OBJECT); + OSON_EVENT_TO_JSON_TOKEN.put(OracleJsonParser.Event.VALUE_INTERVALDS, + JsonToken.VALUE_STRING); + OSON_EVENT_TO_JSON_TOKEN.put(OracleJsonParser.Event.VALUE_INTERVALYM, + JsonToken.VALUE_STRING); } /** @@ -140,18 +158,19 @@ protected void _closeInput() throws IOException { private JsonToken fromOsonEvent(Event event) { switch (event) { case KEY_NAME: - if(DEBUG) logger.log(Level.FINEST, "KEY_NAME> " + parser.getString()); + logger.log(Level.FINEST, "KEY_NAME> " + parser.getString()); this.fieldName = parser.getString(); return JsonToken.FIELD_NAME; case VALUE_DECIMAL: - if(DEBUG) logger.log(Level.FINEST, "VALUE_DECIMAL> " + parser.getBigDecimal()+" / "+parser.isIntegralNumber()); + logger.log(Level.FINEST, "VALUE_DECIMAL> " + + parser.getBigDecimal()+" / "+parser.isIntegralNumber()); return parser.isIntegralNumber() ? JsonToken.VALUE_NUMBER_INT : JsonToken.VALUE_NUMBER_FLOAT; default: JsonToken token = OSON_EVENT_TO_JSON_TOKEN.get(currentEvent); if(token == null) throw new IllegalStateException("Invalid event " + currentEvent); - if(DEBUG) logger.log(Level.FINEST, token.toString()); + logger.log(Level.FINEST, token.toString()); return token; } } @@ -164,7 +183,7 @@ private JsonToken fromOsonEvent(Event event) { */ @Override public JsonToken nextToken() throws IOException { - if(DEBUG) logger.log(Level.FINEST, "nextToken"); + logger.log(Level.FINEST, "nextToken"); if (parser.hasNext()) { currentEvent = parser.next(); _currToken = fromOsonEvent(currentEvent); @@ -206,7 +225,7 @@ public void close() throws IOException { @Override public JsonToken nextValue() throws IOException { - if(DEBUG) logger.log(Level.FINEST, "nextValue"); + logger.log(Level.FINEST, "nextValue"); return super.nextValue(); } @@ -218,7 +237,7 @@ public JsonToken nextValue() throws IOException { */ @Override public JsonParser skipChildren() throws IOException { - if(DEBUG) logger.log(Level.FINEST, "skipChildren"); + logger.log(Level.FINEST, "skipChildren"); if (currentEvent == null && parser.hasNext()) { currentEvent = parser.next(); } @@ -267,7 +286,7 @@ public boolean isExpectedStartArrayToken() { */ @Override public JsonToken getCurrentToken() { - if(DEBUG) logger.log(Level.FINEST, "getCurrentToken"); + logger.log(Level.FINEST, "getCurrentToken"); if (currentEvent == null && parser.hasNext()) { currentEvent = parser.next(); } @@ -277,7 +296,7 @@ public JsonToken getCurrentToken() { /** * Get the current token. - * @return + * @return The current JsonToken. */ @Override public JsonToken currentToken() { @@ -297,7 +316,7 @@ public int getCurrentTokenId() { */ @Override public int currentTokenId() { - if(DEBUG) logger.log(Level.FINEST, "getCurrentTokenId"); + logger.log(Level.FINEST, "getCurrentTokenId"); JsonToken jt; if ((jt = OSON_EVENT_TO_JSON_TOKEN.get(currentEvent)) != null) { return jt.id(); @@ -317,7 +336,7 @@ public OracleJsonParser.Event currentOsonEvent() { */ @Override public boolean hasCurrentToken() { - if(DEBUG) logger.log(Level.FINEST, "hasCurrentToken"); + logger.log(Level.FINEST, "hasCurrentToken"); return currentEvent != null; } @@ -326,7 +345,7 @@ public boolean hasCurrentToken() { */ @Override public boolean hasTokenId(final int id) { - if(DEBUG) logger.log(Level.FINEST, "hasTokenId( " + id + " )"); + logger.log(Level.FINEST, "hasTokenId( " + id + " )"); if (id == JsonTokenId.ID_FIELD_NAME) { return currentEvent == OracleJsonParser.Event.KEY_NAME; } @@ -345,13 +364,15 @@ public boolean hasTokenId(final int id) { */ @Override public boolean hasToken(JsonToken jsonToken) { - if(DEBUG) logger.log(Level.FINEST, "hasToken( " + jsonToken + " )"); + logger.log(Level.FINEST, "hasToken( " + jsonToken + " )"); JsonToken jt = OSON_EVENT_TO_JSON_TOKEN.get(currentEvent); if (jt != null) { return jt.id() == jsonToken.id(); } else if( currentEvent == OracleJsonParser.Event.VALUE_DECIMAL ) { - return parser.isIntegralNumber() ? JsonToken.VALUE_NUMBER_INT.id() == jsonToken.id() : JsonToken.VALUE_NUMBER_FLOAT.id() == jsonToken.id(); + return parser.isIntegralNumber() + ? JsonToken.VALUE_NUMBER_INT.id() == jsonToken.id() + : JsonToken.VALUE_NUMBER_FLOAT.id() == jsonToken.id(); } return false; @@ -371,7 +392,7 @@ public void clearCurrentToken() { */ @Override public JsonToken getLastClearedToken() { - if(DEBUG) logger.log(Level.FINEST, "getLastClearedToken"); + logger.log(Level.FINEST, "getLastClearedToken"); if (lastClearedEvent == null) { return null; } @@ -403,7 +424,7 @@ public String getCurrentName() throws IOException { */ @Override public String getText() throws IOException { - if(DEBUG) logger.log(Level.FINEST, "getText"); + logger.log(Level.FINEST, "getText"); return parser.getString(); } @@ -413,7 +434,7 @@ public String getText() throws IOException { //discuss @Override public char[] getTextCharacters() throws IOException { - if(DEBUG) logger.log(Level.FINEST, "getTextCharacters"); + logger.log(Level.FINEST, "getTextCharacters"); return parser.getString().toCharArray(); } @@ -423,7 +444,7 @@ public char[] getTextCharacters() throws IOException { //discuss @Override public int getTextLength() throws IOException { - if(DEBUG) logger.log(Level.FINEST, "getTextLength"); + logger.log(Level.FINEST, "getTextLength"); return parser.getString().length(); } @@ -433,7 +454,7 @@ public int getTextLength() throws IOException { // discuss @Override public int getTextOffset() throws IOException { - if(DEBUG) logger.log(Level.FINEST, "getTextOffset"); + logger.log(Level.FINEST, "getTextOffset"); return 0; } @@ -442,7 +463,7 @@ public int getTextOffset() throws IOException { */ @Override public Number getNumberValue() throws IOException { - if(DEBUG) logger.log(Level.FINEST, "getNumberValue"); + logger.log(Level.FINEST, "getNumberValue"); return parser.getBigDecimal(); } @@ -451,7 +472,7 @@ public Number getNumberValue() throws IOException { */ @Override public NumberType getNumberType() throws IOException { - if(DEBUG) logger.log(Level.FINEST, "getNumberType "+parser.isIntegralNumber()); + logger.log(Level.FINEST, "getNumberType "+parser.isIntegralNumber()); switch(currentEvent) { case VALUE_FLOAT: return NumberType.FLOAT; @@ -467,7 +488,7 @@ public NumberType getNumberType() throws IOException { */ @Override public int getIntValue() throws IOException { - if(DEBUG) logger.log(Level.FINEST, "getIntValue"); + logger.log(Level.FINEST, "getIntValue"); return parser.getInt(); } @@ -476,7 +497,7 @@ public int getIntValue() throws IOException { */ @Override public long getLongValue() throws IOException { - if(DEBUG) logger.log(Level.FINEST, "getLongValue"); + logger.log(Level.FINEST, "getLongValue"); return parser.getLong(); } @@ -518,8 +539,10 @@ public BigDecimal getDecimalValue() throws IOException { //discuss @Override public byte[] getBinaryValue(Base64Variant base64Variant) throws IOException { - if(DEBUG) logger.log(Level.FINEST, "getBinaryValue"); - byte[] result = currentEvent == Event.VALUE_BINARY ? parser.getBytes(): super.getBinaryValue(base64Variant); + logger.log(Level.FINEST, "getBinaryValue"); + byte[] result = currentEvent == Event.VALUE_BINARY + ? parser.getBytes() + : super.getBinaryValue(base64Variant); _binaryValue = null; return result; } @@ -529,7 +552,7 @@ public byte[] getBinaryValue(Base64Variant base64Variant) throws IOException { */ @Override public String getValueAsString(String s) throws IOException { - if(DEBUG) logger.log(Level.FINEST, "getValueAsString"); + logger.log(Level.FINEST, "getValueAsString"); if (currentToken() == JsonToken.FIELD_NAME) { return fieldName; } @@ -538,19 +561,6 @@ public String getValueAsString(String s) throws IOException { } } - /** - * Reads a LocalDateTime value from the OracleJsonParser. - * - * @return The LocalDateTime value parsed. - */ - public LocalDateTime readLocalDateTime() { - if(DEBUG) logger.log(Level.FINEST, "readLocalDateTime " + currentEvent); - if(currentEvent == OracleJsonParser.Event.VALUE_STRING) { - return LocalDateTime.parse( parser.getString() ); - } else { - return parser.getLocalDateTime(); - } - } /** * Reads an OffsetDateTime value from the OracleJsonParser. * @@ -558,7 +568,7 @@ public LocalDateTime readLocalDateTime() { */ public OffsetDateTime readOffsetDateTime() { - if(DEBUG) logger.log(Level.FINEST, "readOffsetDateTime " + currentEvent); + logger.log(Level.FINEST, "readOffsetDateTime " + currentEvent); if(currentEvent == OracleJsonParser.Event.VALUE_STRING) { return OffsetDateTime.parse( parser.getString() ); } else { @@ -572,7 +582,7 @@ public OffsetDateTime readOffsetDateTime() { * @return The Duration value parsed. */ public Duration readDuration() { - if(DEBUG) logger.log(Level.FINEST, "readDuration " + currentEvent); + logger.log(Level.FINEST, "readDuration " + currentEvent); if(currentEvent == OracleJsonParser.Event.VALUE_STRING) { return Duration.parse( parser.getString() ); } else { @@ -586,7 +596,7 @@ public Duration readDuration() { * @return The Period value parsed. */ public Period readPeriod() { - if(DEBUG) logger.log(Level.FINEST,"readPeriod " + currentEvent); + logger.log(Level.FINEST,"readPeriod " + currentEvent); if(currentEvent == OracleJsonParser.Event.VALUE_STRING) { return Period.parse( parser.getString() ); } else { @@ -594,8 +604,17 @@ public Period readPeriod() { } } - public LocalDateTime getLocalDateTime() { - if(DEBUG) logger.log(Level.FINEST, "getLocalDateTime"); - return parser.getLocalDateTime(); + /** + * Reads a LocalDateTime value from the OracleJsonParser. + * + * @return The LocalDateTime value parsed. + */ + public LocalDateTime readLocalDateTime() { + logger.log(Level.FINEST, "readLocalDateTime " + currentEvent); + if(currentEvent == OracleJsonParser.Event.VALUE_STRING) { + return LocalDateTime.parse( parser.getString() ); + } else { + return parser.getLocalDateTime(); + } } } diff --git a/ojdbc-provider-jackson-oson/src/main/java/oracle/jdbc/provider/oson/Util.java b/ojdbc-provider-jackson-oson/src/main/java/oracle/jdbc/provider/oson/Util.java index 426e9b82..7cb3a488 100644 --- a/ojdbc-provider-jackson-oson/src/main/java/oracle/jdbc/provider/oson/Util.java +++ b/ojdbc-provider-jackson-oson/src/main/java/oracle/jdbc/provider/oson/Util.java @@ -1,3 +1,41 @@ +/* + ** Copyright (c) 2024 Oracle and/or its affiliates. + ** + ** The Universal Permissive License (UPL), Version 1.0 + ** + ** Subject to the condition set forth below, permission is hereby granted to any + ** person obtaining a copy of this software, associated documentation and/or data + ** (collectively the "Software"), free of charge and under any and all copyright + ** rights in the Software, and any and all patent rights owned or freely + ** licensable by each licensor hereunder covering either (i) the unmodified + ** Software as contributed to or provided by such licensor, or (ii) the Larger + ** Works (as defined below), to deal in both + ** + ** (a) the Software, and + ** (b) any piece of software and/or hardware listed in the lrgrwrks.txt file if + ** one is included with the Software (each a "Larger Work" to which the Software + ** is contributed by such licensors), + ** + ** without restriction, including without limitation the rights to copy, create + ** derivative works of, display, perform, and distribute the Software and make, + ** use, sell, offer for sale, import, export, have made, and have sold the + ** Software and the Larger Work(s), and to sublicense the foregoing rights on + ** either these or other terms. + ** + ** This license is subject to the following condition: + ** The above copyright notice and either this complete permission notice or at + ** a minimum a reference to the UPL must be included in all copies or + ** substantial portions of the Software. + ** + ** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + ** IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + ** FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + ** AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + ** LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + ** OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + ** SOFTWARE. + */ + package oracle.jdbc.provider.oson; import com.fasterxml.jackson.databind.JavaType; @@ -6,41 +44,73 @@ import java.io.Serializable; import java.util.List; +/** + * Utility class providing helper methods for identifying and handling + * serializable types and Java wrapper types. This class is designed to assist + * in determining if a class or interface implements {@link Serializable}, and + * whether a type falls under standard Java types that are inherently serializable. + */ public class Util { - public static boolean isJavaSerializableType(Class clazz) { - String packageName = clazz.getPackage().getName(); - - if (packageName.startsWith("java.lang") - || packageName.startsWith("java.util") - || packageName.startsWith("java.sql") - || packageName.startsWith("java.time") - || packageName.startsWith("java.math") - || packageName.startsWith("java.security") - || packageName.startsWith("java.net") - || clazz.isArray()) { - return true; - } - - return false; - } - public static boolean implementsSerializable(List interfaces) { - boolean result = false; - for (JavaType javaType : interfaces) { - if(Serializable.class.isAssignableFrom(javaType.getRawClass())){ - result = true; - } - } - return result; - } + /** + * Checks if a given class belongs to the standard Java packages or is an array, + * which are typically considered inherently serializable. + * + * @param clazz the class to check. + * @return {@code true} if the class is from a known Java package or is an array; + * {@code false} otherwise. + */ + public static boolean isJavaSerializableType(Class clazz) { + String packageName = clazz.getPackage().getName(); - public static boolean isJavaWrapperSerializable(BeanPropertyWriter writer) { - JavaType type = writer.getType(); - return isJavaWrapperSerializable(type); - } + return packageName.startsWith("java.lang") + || packageName.startsWith("java.util") + || packageName.startsWith("java.sql") + || packageName.startsWith("java.time") + || packageName.startsWith("java.math") + || packageName.startsWith("java.security") + || packageName.startsWith("java.net") + || clazz.isArray(); + } - public static boolean isJavaWrapperSerializable(JavaType type) { - Class rawType = type.getRawClass(); - return isJavaSerializableType(rawType); + /** + * Checks if any of the provided interfaces implement {@link Serializable}. + * + * @param interfaces the list of {@link JavaType} interfaces to check. + * @return {@code true} if at least one interface implements {@link Serializable}; + * {@code false} otherwise. + */ + public static boolean implementsSerializable(List interfaces) { + boolean result = false; + for (JavaType javaType : interfaces) { + if(Serializable.class.isAssignableFrom(javaType.getRawClass())){ + result = true; + break; + } } + return result; + } + + /** + * Determines if a given field represented by a {@link BeanPropertyWriter} is + * inherently serializable as a Java wrapper type. + * + * @param writer the {@link BeanPropertyWriter} representing the field to check. + * @return {@code true} if the field type is inherently serializable; {@code false} otherwise. + */ + public static boolean isJavaWrapperSerializable(BeanPropertyWriter writer) { + JavaType type = writer.getType(); + return isJavaWrapperSerializable(type); + } + + /** + * Checks if a given {@link JavaType} represents a Java wrapper type that is inherently serializable. + * + * @param type the {@link JavaType} to check. + * @return {@code true} if the type is inherently serializable; {@code false} otherwise. + */ + public static boolean isJavaWrapperSerializable(JavaType type) { + Class rawType = type.getRawClass(); + return isJavaSerializableType(rawType); + } } diff --git a/ojdbc-provider-jackson-oson/src/main/java/oracle/jdbc/provider/oson/deser/OsonBigIntegerDeserializer.java b/ojdbc-provider-jackson-oson/src/main/java/oracle/jdbc/provider/oson/deser/OsonBigIntegerDeserializer.java index 3768f4be..6b2031d7 100644 --- a/ojdbc-provider-jackson-oson/src/main/java/oracle/jdbc/provider/oson/deser/OsonBigIntegerDeserializer.java +++ b/ojdbc-provider-jackson-oson/src/main/java/oracle/jdbc/provider/oson/deser/OsonBigIntegerDeserializer.java @@ -88,7 +88,6 @@ protected OsonBigIntegerDeserializer() { public BigInteger deserialize(JsonParser p, DeserializationContext ctxt) throws IOException{ if( p instanceof OsonParser) { final OsonParser _parser = (OsonParser)p; - return _parser.getBigIntegerValue(); } else { diff --git a/ojdbc-provider-jackson-oson/src/main/java/oracle/jdbc/provider/oson/deser/OsonBooleanDeserializer.java b/ojdbc-provider-jackson-oson/src/main/java/oracle/jdbc/provider/oson/deser/OsonBooleanDeserializer.java index b4531eac..41ef04a3 100644 --- a/ojdbc-provider-jackson-oson/src/main/java/oracle/jdbc/provider/oson/deser/OsonBooleanDeserializer.java +++ b/ojdbc-provider-jackson-oson/src/main/java/oracle/jdbc/provider/oson/deser/OsonBooleanDeserializer.java @@ -41,38 +41,72 @@ import com.fasterxml.jackson.core.JacksonException; import com.fasterxml.jackson.core.JsonParser; import com.fasterxml.jackson.databind.DeserializationContext; -import com.fasterxml.jackson.databind.deser.std.NumberDeserializers; import com.fasterxml.jackson.databind.deser.std.NumberDeserializers.BooleanDeserializer; import com.fasterxml.jackson.databind.deser.std.StdScalarDeserializer; import oracle.jdbc.provider.oson.OsonParser; import java.io.IOException; - +/** + * Custom deserializer for Boolean values that supports additional formats for parsing. + * This class is designed to handle specific input values such as "Y" and "N" and map them + * to `Boolean.TRUE` and `Boolean.FALSE` respectively. For other values, it delegates + * deserialization to a wrapped Boolean deserializer. + * + *

    This implementation is especially useful when working with JSON input that uses + * unconventional representations of Boolean values.

    + * + * + * @see StdScalarDeserializer + */ public class OsonBooleanDeserializer extends StdScalarDeserializer { - public static final OsonBooleanDeserializer INSTANCE = new OsonBooleanDeserializer(); - public static final BooleanDeserializer WRAPPER_INSTAMCE = new BooleanDeserializer(Boolean.class,null); + /** + * Singleton instance of the deserializer for direct usage. + */ + public static final OsonBooleanDeserializer INSTANCE = new OsonBooleanDeserializer(); - protected OsonBooleanDeserializer() { - super(Boolean.class); - } + /** + * Wrapped Boolean deserializer instance for handling standard deserialization cases. + */ + public static final BooleanDeserializer WRAPPER_INSTANCE = new BooleanDeserializer(Boolean.class,null); + + /** + * Protected constructor to enforce singleton pattern. + * Initializes the deserializer with the `Boolean` type. + */ + protected OsonBooleanDeserializer() { + super(Boolean.class); + } - @Override - public Boolean deserialize(JsonParser p, DeserializationContext ctxt) throws IOException, JacksonException { - if (p instanceof OsonParser) { - switch (p.getCurrentToken()) { - case VALUE_STRING: - { - String str = p.getText(); - if (str.equalsIgnoreCase("Y")) { - return Boolean.TRUE; - } else if (str.equalsIgnoreCase("N")) { - return Boolean.FALSE; - } - } - default: - return WRAPPER_INSTAMCE.deserialize(p, ctxt); - } + /** + * Deserializes a JSON token into a Boolean value. + * + *

    If the token is a string and its value matches "Y" (case-insensitive), it returns + * `Boolean.TRUE`. If the value matches "N" (case-insensitive), it returns `Boolean.FALSE`. + * For all other cases, the method delegates the deserialization to `WRAPPER_INSTAMCE`.

    + * + * @param p the JSON parser + * @param ctxt the deserialization context + * @return the deserialized Boolean value + * @throws IOException if a low-level I/O problem occurs + * @throws JacksonException if there is a problem with deserialization + */ + @Override + public Boolean deserialize(JsonParser p, DeserializationContext ctxt) throws IOException, JacksonException { + if (p instanceof OsonParser) { + switch (p.getCurrentToken()) { + case VALUE_STRING: + { + String str = p.getText(); + if (str.equalsIgnoreCase("Y")) { + return Boolean.TRUE; + } else if (str.equalsIgnoreCase("N")) { + return Boolean.FALSE; + } } - return WRAPPER_INSTAMCE.deserialize(p, ctxt); + default: + return WRAPPER_INSTANCE.deserialize(p, ctxt); + } } + return WRAPPER_INSTANCE.deserialize(p, ctxt); + } } diff --git a/ojdbc-provider-jackson-oson/src/main/java/oracle/jdbc/provider/oson/deser/OsonByteDeserializer.java b/ojdbc-provider-jackson-oson/src/main/java/oracle/jdbc/provider/oson/deser/OsonByteDeserializer.java index fba83b79..cd53379f 100644 --- a/ojdbc-provider-jackson-oson/src/main/java/oracle/jdbc/provider/oson/deser/OsonByteDeserializer.java +++ b/ojdbc-provider-jackson-oson/src/main/java/oracle/jdbc/provider/oson/deser/OsonByteDeserializer.java @@ -63,35 +63,35 @@ */ public class OsonByteDeserializer extends StdScalarDeserializer { /** - * A singleton instance of the deserializer. - */ + * A singleton instance of the deserializer. + */ public static final OsonByteDeserializer INSTANCE = new OsonByteDeserializer(); /** - * Default constructor that initializes the deserializer for the {@link byte[]} class. - */ + * Default constructor that initializes the deserializer for the {@link byte[]} class. + */ protected OsonByteDeserializer() { - super(Year.class); + super(Year.class); } /** - * Deserializes a byte array from the JSON input using the{@link OsonParser}. - * - * @param p the {@link JsonParser} for reading the JSON content - * @param ctxt the deserialization context - * @return the deserialized byte array - * @throws IOException if there is a problem with reading the input - */ + * Deserializes a byte array from the JSON input using the{@link OsonParser}. + * + * @param p the {@link JsonParser} for reading the JSON content + * @param ctxt the deserialization context + * @return the deserialized byte array + * @throws IOException if there is a problem with reading the input + */ @Override public byte[] deserialize(JsonParser p, DeserializationContext ctxt) throws IOException { - if(p instanceof OsonParser && - (p.currentToken().equals(JsonToken.VALUE_EMBEDDED_OBJECT))) { - final OsonParser _parser = (OsonParser)p; + if(p instanceof OsonParser && + (p.currentToken().equals(JsonToken.VALUE_EMBEDDED_OBJECT))) { + final OsonParser _parser = (OsonParser)p; - return _parser.getBinaryValue(); - } else { - return (byte[]) PrimitiveArrayDeserializers.forType(byte.class).deserialize(p,ctxt); - } + return _parser.getBinaryValue(); + } else { + return (byte[]) PrimitiveArrayDeserializers.forType(byte.class).deserialize(p,ctxt); + } } diff --git a/ojdbc-provider-jackson-oson/src/main/java/oracle/jdbc/provider/oson/deser/OsonConverterArrayDeserializer.java b/ojdbc-provider-jackson-oson/src/main/java/oracle/jdbc/provider/oson/deser/OsonConverterArrayDeserializer.java index 67489a83..2da94660 100644 --- a/ojdbc-provider-jackson-oson/src/main/java/oracle/jdbc/provider/oson/deser/OsonConverterArrayDeserializer.java +++ b/ojdbc-provider-jackson-oson/src/main/java/oracle/jdbc/provider/oson/deser/OsonConverterArrayDeserializer.java @@ -49,41 +49,79 @@ import java.lang.reflect.*; import java.util.ArrayList; +/** + * A custom deserializer for arrays of elements that use a specified `AttributeConverter` to + * handle the deserialization of individual elements. This class integrates with the + * `OsonConverterDeserializer` to support complex or custom mappings for arrays. + * + * @param the type of the array elements + */ public class OsonConverterArrayDeserializer extends JsonDeserializer { - private final Class elementType; - private final OsonConverterDeserializer deserializer; - - public OsonConverterArrayDeserializer(Class converter) { - elementType = resolveType(converter); - deserializer = new OsonConverterDeserializer<>(converter); - } + /** + * The type of the elements in the array. + */ + private final Class elementType; - private Class resolveType(Class converter) { - Class iter = converter; - do{ - Method[] methods = iter.getDeclaredMethods(); - for(Method method : methods) { - if (method.getName().equals("convertToEntityAttribute") - && method.getReturnType() != Object.class){ - return method.getReturnType(); - } - } - iter = iter.getSuperclass(); - }while(iter.getSuperclass() != null); + /** + * The deserializer used to process individual elements of the array. + */ + private final OsonConverterDeserializer deserializer; - return Object.class; - } + /** + * Constructs a deserializer for an array of elements with a given `AttributeConverter`. + * + * @param converter the class of the `AttributeConverter` to use for element deserialization + */ + public OsonConverterArrayDeserializer(Class converter) { + elementType = resolveType(converter); + deserializer = new OsonConverterDeserializer<>(converter); + } - @Override - public T[] deserialize(JsonParser p, DeserializationContext ctxt) throws IOException, JacksonException { - ArrayList result = new ArrayList<>(); - if (p.getCurrentToken() == JsonToken.START_ARRAY) { - while (p.nextToken() != JsonToken.END_ARRAY) { - result.add(deserializer.deserialize(p, ctxt)); - } + /** + * Resolves the element type of the array by examining the `convertToEntityAttribute` + * method of the provided `AttributeConverter`. + * + * @param converter the class of the `AttributeConverter` + * @return the return type of the `convertToEntityAttribute` method, or `Object.class` if not resolvable + */ + private Class resolveType(Class converter) { + Class iter = converter; + do{ + Method[] methods = iter.getDeclaredMethods(); + for(Method method : methods) { + if (method.getName().equals("convertToEntityAttribute") + && method.getReturnType() != Object.class){ + return method.getReturnType(); } - T[] array = (T[]) Array.newInstance(elementType, result.size()); - return result.toArray(array); + } + iter = iter.getSuperclass(); + }while(iter.getSuperclass() != null); + + return Object.class; + } + + /** + * Deserializes JSON input into an array of elements of type `T` using the custom converter. + * + *

    The method processes JSON arrays and delegates the deserialization of individual + * elements to the `OsonConverterDeserializer`.

    + * + * @param p the JSON parser + * @param ctxt the deserialization context + * @return the deserialized array of elements + * @throws IOException if a low-level I/O problem occurs + * @throws JacksonException if there is a problem with deserialization + */ + @Override + public T[] deserialize(JsonParser p, DeserializationContext ctxt) throws IOException, JacksonException { + ArrayList result = new ArrayList<>(); + if (p.getCurrentToken() == JsonToken.START_ARRAY) { + while (p.nextToken() != JsonToken.END_ARRAY) { + result.add(deserializer.deserialize(p, ctxt)); + } } + T[] array = (T[]) Array.newInstance(elementType, result.size()); + return result.toArray(array); + } } diff --git a/ojdbc-provider-jackson-oson/src/main/java/oracle/jdbc/provider/oson/deser/OsonConverterDeserializer.java b/ojdbc-provider-jackson-oson/src/main/java/oracle/jdbc/provider/oson/deser/OsonConverterDeserializer.java index e21b6572..0c9487c9 100644 --- a/ojdbc-provider-jackson-oson/src/main/java/oracle/jdbc/provider/oson/deser/OsonConverterDeserializer.java +++ b/ojdbc-provider-jackson-oson/src/main/java/oracle/jdbc/provider/oson/deser/OsonConverterDeserializer.java @@ -55,116 +55,166 @@ import java.time.LocalDateTime; import java.time.LocalTime; +/** + * A generic deserializer that integrates with a custom `AttributeConverter` for deserializing + * JSON input into Java objects. This class is useful for handling complex or custom mappings + * of JSON data types to entity attributes in Java. + * + * @param the type of the entity attribute to deserialize into + */ public class OsonConverterDeserializer extends JsonDeserializer { - private final AttributeConverter converter; - private final Class elementReturnType; - private final Class elementInputType; - - public OsonConverterDeserializer(Class converter) { - try { - this.converter = converter.getConstructor().newInstance(); - this.elementReturnType = resolveReturnType(converter);; - this.elementInputType = resolveInputType(converter); - } catch (InstantiationException - | IllegalAccessException - | InvocationTargetException - | NoSuchMethodException e) { - throw new RuntimeException(e); - } - } + /** + * The attribute converter instance used for the deserialization process. + */ + private final AttributeConverter converter; - private Class resolveInputType(Class converter) { - Class iter = converter; - do{ - Method[] methods = iter.getDeclaredMethods(); - for(Method method : methods) { - if (method.getName().equals("convertToDatabaseColumn") - && method.getReturnType() != Object.class){ - Class returnType = (Class) method.getReturnType(); - return returnType; - } - } - iter = iter.getSuperclass(); - }while(iter.getSuperclass() != null); + /** + * The return type of the `convertToEntityAttribute` method in the converter. + */ + private final Class elementReturnType; + + /** + * The return type of the `convertToEntityAttribute` method in the converter. + */ + private final Class elementInputType; - return (Class) Object.class; + /** + * Constructs an instance of the deserializer for a given `AttributeConverter` class. + * + * @param converter the class of the `AttributeConverter` to use + * @throws RuntimeException if the converter cannot be instantiated or its methods cannot + * be resolved + */ + public OsonConverterDeserializer(Class converter) { + try { + this.converter = converter.getConstructor().newInstance(); + this.elementReturnType = resolveReturnType(converter);; + this.elementInputType = resolveInputType(converter); + } catch (InstantiationException + | IllegalAccessException + | InvocationTargetException + | NoSuchMethodException e) { + throw new RuntimeException(e); } + } - private Class resolveReturnType(Class converter) { - Class iter = converter; - do{ - Method[] methods = iter.getDeclaredMethods(); - for(Method method : methods) { - if (method.getName().equals("convertToEntityAttribute") - && method.getReturnType() != Object.class){ - Class returnType = (Class) method.getReturnType(); - return returnType; - } - } - iter = iter.getSuperclass(); - }while(iter.getSuperclass() != null); + /** + * Resolves the input type of the `convertToDatabaseColumn` method from the given converter class. + * + * @param converter the class of the `AttributeConverter` + * @return the input type of the method, or `Object.class` if not resolvable + */ + private Class resolveInputType(Class converter) { + Class iter = converter; + do{ + Method[] methods = iter.getDeclaredMethods(); + for(Method method : methods) { + if (method.getName().equals("convertToDatabaseColumn") + && method.getReturnType() != Object.class){ + Class returnType = (Class) method.getReturnType(); + return returnType; + } + } + iter = iter.getSuperclass(); + }while(iter.getSuperclass() != null); - return (Class) Object.class; - } - - @Override - public T deserialize(JsonParser p, DeserializationContext ctxt) throws IOException, JacksonException { - T result = null; - switch (p.getCurrentToken()) { - - case VALUE_NUMBER_INT: - int pInt = p.getIntValue(); - result = ((T) converter.convertToEntityAttribute(pInt)); - break; - case VALUE_STRING: - { - OsonParser parser= (OsonParser)p; - switch (parser.currentOsonEvent()) { - case VALUE_TIMESTAMP: - LocalDateTime dateTime = parser.getLocalDateTime(); - Timestamp timestamp = Timestamp.valueOf(dateTime); - result = (T) converter.convertToEntityAttribute(timestamp); - break; - case VALUE_DATE: - LocalDate localDate = parser.getLocalDateTime().toLocalDate(); - Date date = Date.valueOf(localDate); - result = (T) converter.convertToEntityAttribute(date); - break; - - default: - String pString = p.getText(); - if (elementInputType.equals(Time.class)) { - result = (T) converter.convertToEntityAttribute(Time.valueOf(pString)); - }else if (elementInputType.equals(LocalTime.class)) { - result = (T) converter.convertToEntityAttribute(LocalTime.parse(pString)); - } - else { - if (pString.length() == 1) { - result = ((T) converter.convertToEntityAttribute(Character.valueOf(pString.charAt(0)))); - break; - } - result = ((T) converter.convertToEntityAttribute(pString)); - } - break; - } - break; + return (Class) Object.class; + } + + /** + * Resolves the return type of the `convertToEntityAttribute` method from the given converter class. + * + * @param converter the class of the `AttributeConverter` + * @return the return type of the method, or `Object.class` if not resolvable + */ + private Class resolveReturnType(Class converter) { + Class iter = converter; + do{ + Method[] methods = iter.getDeclaredMethods(); + for(Method method : methods) { + if (method.getName().equals("convertToEntityAttribute") + && method.getReturnType() != Object.class){ + Class returnType = (Class) method.getReturnType(); + return returnType; + } + } + iter = iter.getSuperclass(); + }while(iter.getSuperclass() != null); + + return (Class) Object.class; + } + + /** + * Deserializes JSON input into an entity attribute of type `T` using the custom converter. + * + *

    The method handles various token types, including integers, strings, timestamps, + * dates, embedded objects, and more, mapping them to the appropriate attribute type + * using the `AttributeConverter`.

    + * + * @param p the JSON parser + * @param ctxt the deserialization context + * @return the deserialized entity attribute + * @throws IOException if a low-level I/O problem occurs + * @throws JacksonException if there is a problem with deserialization + */ + @Override + public T deserialize(JsonParser p, DeserializationContext ctxt) throws IOException, JacksonException { + T result = null; + switch (p.getCurrentToken()) { + + case VALUE_NUMBER_INT: + int pInt = p.getIntValue(); + result = ((T) converter.convertToEntityAttribute(pInt)); + break; + case VALUE_STRING: + { + OsonParser parser= (OsonParser)p; + switch (parser.currentOsonEvent()) { + case VALUE_TIMESTAMP: + LocalDateTime dateTime = parser.readLocalDateTime(); + Timestamp timestamp = Timestamp.valueOf(dateTime); + result = (T) converter.convertToEntityAttribute(timestamp); + break; + case VALUE_DATE: + LocalDate localDate = parser.readLocalDateTime().toLocalDate(); + Date date = Date.valueOf(localDate); + result = (T) converter.convertToEntityAttribute(date); + break; + + default: + String pString = p.getText(); + if (elementInputType.equals(Time.class)) { + result = (T) converter.convertToEntityAttribute(Time.valueOf(pString)); + }else if (elementInputType.equals(LocalTime.class)) { + result = (T) converter.convertToEntityAttribute(LocalTime.parse(pString)); } - case VALUE_EMBEDDED_OBJECT: - byte[] bytes = p.getBinaryValue(); - result = (T) converter.convertToEntityAttribute(bytes); + else { + if (pString.length() == 1) { + result = ((T) converter.convertToEntityAttribute(Character.valueOf(pString.charAt(0)))); break; + } + result = ((T) converter.convertToEntityAttribute(pString)); + } + break; + } + break; + } + case VALUE_EMBEDDED_OBJECT: + byte[] bytes = p.getBinaryValue(); + result = (T) converter.convertToEntityAttribute(bytes); + break; - default: - String pDString = p.getText(); - if (pDString.length() == 1) { - result = ((T) converter.convertToEntityAttribute(Character.valueOf(pDString.charAt(0)))); - break; - } - result = ((T) converter.convertToEntityAttribute(pDString)); - + default: + String pDString = p.getText(); + if (pDString.length() == 1) { + result = ((T) converter.convertToEntityAttribute(Character.valueOf(pDString.charAt(0)))); + break; } - return result; + result = ((T) converter.convertToEntityAttribute(pDString)); + } + return result; + } } diff --git a/ojdbc-provider-jackson-oson/src/main/java/oracle/jdbc/provider/oson/deser/OsonDateDeserializer.java b/ojdbc-provider-jackson-oson/src/main/java/oracle/jdbc/provider/oson/deser/OsonDateDeserializer.java index 7550b2ca..4af2380e 100644 --- a/ojdbc-provider-jackson-oson/src/main/java/oracle/jdbc/provider/oson/deser/OsonDateDeserializer.java +++ b/ojdbc-provider-jackson-oson/src/main/java/oracle/jdbc/provider/oson/deser/OsonDateDeserializer.java @@ -48,7 +48,6 @@ import com.fasterxml.jackson.databind.introspect.AnnotatedMember; import java.io.IOException; -import java.math.BigDecimal; import java.sql.Time; import java.sql.Timestamp; import java.text.DateFormat; @@ -56,7 +55,7 @@ import java.time.ZoneId; import java.time.format.DateTimeFormatter; import java.util.Date; -import java.util.regex.Pattern; + import jakarta.persistence.Temporal; import jakarta.persistence.TemporalType; import oracle.jdbc.provider.oson.OsonParser; @@ -74,133 +73,133 @@ */ public class OsonDateDeserializer extends DateDeserializers.DateDeserializer { - /** - * Singleton instance of the OsonDateDeserializer with default configuration. - */ - public static final OsonDateDeserializer INSTANCE = new OsonDateDeserializer(false,false); - - /** - * Indicates whether the deserializer should handle temporal timestamps. - */ - private boolean isTemporalTimeStamp = false; - - /** - * Indicates whether the deserializer should handle temporal time strings. - */ - private boolean isTemporalTime = false; - - /** - * Default constructor. Initializes a standard OsonDateDeserializer. - */ - public OsonDateDeserializer() { - super(); - } - - /** - * Constructs a deserializer with specific handling for temporal timestamps or time strings. - * - * @param isTemporalTimeStamp whether to handle temporal timestamps. - * @param isTemporalTime whether to handle temporal time strings. - */ - public OsonDateDeserializer(boolean isTemporalTimeStamp, boolean isTemporalTime) { - this(); - this.isTemporalTimeStamp = isTemporalTimeStamp; - this.isTemporalTime = isTemporalTime; - } - - /** - * Constructs a deserializer with a custom date format. - * - * @param base the base deserializer. - * @param df the custom date format. - * @param formatString the format string used for deserialization. - */ - public OsonDateDeserializer(DateDeserializers.DateDeserializer base, DateFormat df, String formatString) { - super(base, df, formatString); - } - - - @Override - protected OsonDateDeserializer withDateFormat(DateFormat df, String formatString) { - return new OsonDateDeserializer(this, df, formatString); - } - - /** - * Overrides the default method to return an empty `Date` instance with epoch time. - * - * @param ctxt the deserialization context. - * @return a `Date` object representing epoch time (1970-01-01T00:00:00Z). - */ - @Override - public Object getEmptyValue(DeserializationContext ctxt) { - return new Date(0L); + /** + * Singleton instance of the OsonDateDeserializer with default configuration. + */ + public static final OsonDateDeserializer INSTANCE = new OsonDateDeserializer(false,false); + + /** + * Indicates whether the deserializer should handle temporal timestamps. + */ + private boolean isTemporalTimeStamp = false; + + /** + * Indicates whether the deserializer should handle temporal time strings. + */ + private boolean isTemporalTime = false; + + /** + * Default constructor. Initializes a standard OsonDateDeserializer. + */ + public OsonDateDeserializer() { + super(); + } + + /** + * Constructs a deserializer with specific handling for temporal timestamps or time strings. + * + * @param isTemporalTimeStamp whether to handle temporal timestamps. + * @param isTemporalTime whether to handle temporal time strings. + */ + public OsonDateDeserializer(boolean isTemporalTimeStamp, boolean isTemporalTime) { + this(); + this.isTemporalTimeStamp = isTemporalTimeStamp; + this.isTemporalTime = isTemporalTime; + } + + /** + * Constructs a deserializer with a custom date format. + * + * @param base the base deserializer. + * @param df the custom date format. + * @param formatString the format string used for deserialization. + */ + public OsonDateDeserializer(DateDeserializers.DateDeserializer base, DateFormat df, String formatString) { + super(base, df, formatString); + } + + + @Override + protected OsonDateDeserializer withDateFormat(DateFormat df, String formatString) { + return new OsonDateDeserializer(this, df, formatString); + } + + /** + * Overrides the default method to return an empty `Date` instance with epoch time. + * + * @param ctxt the deserialization context. + * @return a `Date` object representing epoch time (1970-01-01T00:00:00Z). + */ + @Override + public Object getEmptyValue(DeserializationContext ctxt) { + return new Date(0L); + } + + /** + * Deserializes a JSON token into a `Date`, `Time`, or `Timestamp` object. + * + * @param p the JSON parser. + * @param ctxt the deserialization context. + * @return the deserialized date object. + * @throws IOException if an I/O error occurs during deserialization. + */ + @Override + public java.util.Date deserialize(JsonParser p, DeserializationContext ctxt) throws IOException { + if(_customFormat != null) { + return super.deserialize(p, ctxt); } - - /** - * Deserializes a JSON token into a `Date`, `Time`, or `Timestamp` object. - * - * @param p the JSON parser. - * @param ctxt the deserialization context. - * @return the deserialized date object. - * @throws IOException if an I/O error occurs during deserialization. - */ - @Override - public java.util.Date deserialize(JsonParser p, DeserializationContext ctxt) throws IOException { - if(_customFormat != null) { - return super.deserialize(p, ctxt); + if( p instanceof OsonParser) { + OsonParser parser = (OsonParser) p; + + if(parser.currentOsonEvent().equals(OracleJsonParser.Event.VALUE_DATE)) { + LocalDateTime dateTime = parser.readLocalDateTime(); + return java.sql.Date.valueOf(dateTime.toLocalDate()); + } + if(parser.currentOsonEvent().equals(OracleJsonParser.Event.VALUE_TIMESTAMP)) { + LocalDateTime dateTime = parser.readLocalDateTime(); + return Timestamp.valueOf(dateTime); + } + switch (p.getCurrentTokenId()) { + case JsonTokenId.ID_STRING: { + if (isTemporalTime) { + return Time.valueOf(p.getText().trim()); + } + String dateTimeString = p.getText().trim(); + LocalDateTime localDateTime = LocalDateTime.parse(dateTimeString, DateTimeFormatter.ISO_LOCAL_DATE_TIME); + return Date.from(localDateTime.atZone(ZoneId.of("UTC")).toInstant()); } - if( p instanceof OsonParser) { - OsonParser parser = (OsonParser) p; - - if(parser.currentOsonEvent().equals(OracleJsonParser.Event.VALUE_DATE)) { - LocalDateTime dateTime = parser.getLocalDateTime(); - return java.sql.Date.valueOf(dateTime.toLocalDate()); - } - if(parser.currentOsonEvent().equals(OracleJsonParser.Event.VALUE_TIMESTAMP)) { - LocalDateTime dateTime = parser.getLocalDateTime(); - return Timestamp.valueOf(dateTime); - } - switch (p.getCurrentTokenId()) { - case JsonTokenId.ID_STRING: { - if (isTemporalTime) { - return Time.valueOf(p.getText().trim()); - } - String dateTimeString = p.getText().trim(); - LocalDateTime localDateTime = LocalDateTime.parse(dateTimeString, DateTimeFormatter.ISO_LOCAL_DATE_TIME); - return Date.from(localDateTime.atZone(ZoneId.of("UTC")).toInstant()); - } - default: return super.deserialize(p, ctxt); - } - } - return super.deserialize(p, ctxt); - + default: return super.deserialize(p, ctxt); + } } - - /** - * Creates a contextual deserializer based on the `@Temporal` annotation. - * - * @param ctxt the deserialization context. - * @param property the bean property being deserialized. - * @return a contextual instance of `OsonDateDeserializer`. - * @throws JsonMappingException if contextual deserialization fails. - */ - @Override - public JsonDeserializer createContextual(DeserializationContext ctxt, BeanProperty property) throws JsonMappingException { - if (property != null) { - AnnotatedMember introspector = property.getMember(); - if (introspector != null) { - Temporal temporal = introspector.getAnnotation(Temporal.class); - if (temporal != null) { - if (temporal.value() == TemporalType.TIMESTAMP) { - return new OsonDateDeserializer(true,false); - } - if (temporal.value() == TemporalType.TIME) { - return new OsonDateDeserializer(false,true); - } - } - } + return super.deserialize(p, ctxt); + + } + + /** + * Creates a contextual deserializer based on the `@Temporal` annotation. + * + * @param ctxt the deserialization context. + * @param property the bean property being deserialized. + * @return a contextual instance of `OsonDateDeserializer`. + * @throws JsonMappingException if contextual deserialization fails. + */ + @Override + public JsonDeserializer createContextual(DeserializationContext ctxt, BeanProperty property) throws JsonMappingException { + if (property != null) { + AnnotatedMember introspector = property.getMember(); + if (introspector != null) { + Temporal temporal = introspector.getAnnotation(Temporal.class); + if (temporal != null) { + if (temporal.value() == TemporalType.TIMESTAMP) { + return new OsonDateDeserializer(true,false); + } + if (temporal.value() == TemporalType.TIME) { + return new OsonDateDeserializer(false,true); + } } - return super.createContextual(ctxt, property); + } } + return super.createContextual(ctxt, property); + } } diff --git a/ojdbc-provider-jackson-oson/src/main/java/oracle/jdbc/provider/oson/deser/OsonLocalDateDeserializer.java b/ojdbc-provider-jackson-oson/src/main/java/oracle/jdbc/provider/oson/deser/OsonLocalDateDeserializer.java index ba230b0b..d86537fb 100644 --- a/ojdbc-provider-jackson-oson/src/main/java/oracle/jdbc/provider/oson/deser/OsonLocalDateDeserializer.java +++ b/ojdbc-provider-jackson-oson/src/main/java/oracle/jdbc/provider/oson/deser/OsonLocalDateDeserializer.java @@ -1,3 +1,40 @@ +/* + ** Copyright (c) 2024 Oracle and/or its affiliates. + ** + ** The Universal Permissive License (UPL), Version 1.0 + ** + ** Subject to the condition set forth below, permission is hereby granted to any + ** person obtaining a copy of this software, associated documentation and/or data + ** (collectively the "Software"), free of charge and under any and all copyright + ** rights in the Software, and any and all patent rights owned or freely + ** licensable by each licensor hereunder covering either (i) the unmodified + ** Software as contributed to or provided by such licensor, or (ii) the Larger + ** Works (as defined below), to deal in both + ** + ** (a) the Software, and + ** (b) any piece of software and/or hardware listed in the lrgrwrks.txt file if + ** one is included with the Software (each a "Larger Work" to which the Software + ** is contributed by such licensors), + ** + ** without restriction, including without limitation the rights to copy, create + ** derivative works of, display, perform, and distribute the Software and make, + ** use, sell, offer for sale, import, export, have made, and have sold the + ** Software and the Larger Work(s), and to sublicense the foregoing rights on + ** either these or other terms. + ** + ** This license is subject to the following condition: + ** The above copyright notice and either this complete permission notice or at + ** a minimum a reference to the UPL must be included in all copies or + ** substantial portions of the Software. + ** + ** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + ** IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + ** FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + ** AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + ** LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + ** OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + ** SOFTWARE. + */ package oracle.jdbc.provider.oson.deser; import com.fasterxml.jackson.annotation.JsonFormat; @@ -12,54 +49,115 @@ import java.time.LocalDateTime; import java.time.format.DateTimeFormatter; +/** + * Custom deserializer for `LocalDate` that extends Jackson's `LocalDateDeserializer`. + * This implementation is specifically designed to handle JSON input in the context of + * `OsonParser` and supports Oracle JSON-specific events like `VALUE_DATE`. + * + *

    It provides additional flexibility by allowing the use of custom `DateTimeFormatter` + * and supports configurations for leniency and shape.

    + */ public class OsonLocalDateDeserializer extends LocalDateDeserializer { - public static final OsonLocalDateDeserializer INSTANCE = new OsonLocalDateDeserializer(); - public OsonLocalDateDeserializer() { - super(); - } - public OsonLocalDateDeserializer(DateTimeFormatter dateFormat) { - super(dateFormat); - } + /** + * Singleton instance for default usage. + */ + public static final OsonLocalDateDeserializer INSTANCE = new OsonLocalDateDeserializer(); + /** + * Default constructor using ISO_LOCAL_DATE as the default formatter. + */ + public OsonLocalDateDeserializer() { + super(); + } - public OsonLocalDateDeserializer(OsonLocalDateDeserializer base, DateTimeFormatter dtf) { - super(base, dtf); - } + /** + * Constructor to initialize with a custom `DateTimeFormatter`. + * + * @param dateFormat the custom `DateTimeFormatter` to use + */ + public OsonLocalDateDeserializer(DateTimeFormatter dateFormat) { + super(dateFormat); + } - public OsonLocalDateDeserializer(OsonLocalDateDeserializer base, Boolean leniency) { - super(base, leniency); - } + /** + * Copy constructor with a custom `DateTimeFormatter`. + * + * @param base the base deserializer + * @param dtf the custom `DateTimeFormatter` + */ + public OsonLocalDateDeserializer(OsonLocalDateDeserializer base, DateTimeFormatter dtf) { + super(base, dtf); + } - public OsonLocalDateDeserializer(OsonLocalDateDeserializer base, JsonFormat.Shape shape) { - super(base, shape); - } + /** + * Copy constructor with leniency configuration. + * + * @param base the base deserializer + * @param leniency the leniency setting + */ + public OsonLocalDateDeserializer(OsonLocalDateDeserializer base, Boolean leniency) { + super(base, leniency); + } - @Override - protected OsonLocalDateDeserializer withDateFormat(DateTimeFormatter dtf) { - return new OsonLocalDateDeserializer(dtf); - } + /** + * Copy constructor with shape configuration. + * + * @param base the base deserializer + * @param shape the JSON format shape + */ + public OsonLocalDateDeserializer(OsonLocalDateDeserializer base, JsonFormat.Shape shape) { + super(base, shape); + } - @Override - protected OsonLocalDateDeserializer withShape(JsonFormat.Shape shape) { - return new OsonLocalDateDeserializer(this,shape); - } + /** + * Creates a new deserializer with the specified `DateTimeFormatter`. + * + * @param dtf the custom `DateTimeFormatter` + * @return a new `OsonLocalDateDeserializer` with the given formatter + */ + @Override + protected OsonLocalDateDeserializer withDateFormat(DateTimeFormatter dtf) { + return new OsonLocalDateDeserializer(dtf); + } - @Override - public LocalDate deserialize(JsonParser parser, DeserializationContext context) throws IOException { - if(_shape != null || _formatter!= DateTimeFormatter.ISO_LOCAL_DATE) - return super.deserialize(parser, context); + /** + * Creates a new deserializer with the specified JSON format shape. + * + * @param shape the JSON format shape + * @return a new `OsonLocalDateDeserializer` with the given shape + */ + @Override + protected OsonLocalDateDeserializer withShape(JsonFormat.Shape shape) { + return new OsonLocalDateDeserializer(this,shape); + } - if (parser instanceof OsonParser) { - OsonParser _parser = (OsonParser) parser; + /** + * Deserializes a JSON input into a `LocalDate` instance. + * + *

    If the input is parsed using `OsonParser` and contains an Oracle JSON-specific + * `VALUE_DATE` event, it processes the input as a `LocalDate`. For all other cases, + * it delegates to the base `LocalDateDeserializer`.

    + * + * @param parser the JSON parser + * @param context the deserialization context + * @return the deserialized `LocalDate` + * @throws IOException if a low-level I/O problem occurs + */ + @Override + public LocalDate deserialize(JsonParser parser, DeserializationContext context) throws IOException { + if(_shape != null || _formatter!= DateTimeFormatter.ISO_LOCAL_DATE) + return super.deserialize(parser, context); - if(_parser.currentOsonEvent().equals(OracleJsonParser.Event.VALUE_DATE)) { - LocalDateTime dateTime = _parser.getLocalDateTime(); - LocalDate localDate = dateTime.toLocalDate(); - return localDate; - } - } - else { - return super.deserialize(parser, context); - } - return super.deserialize(parser, context); + if (parser instanceof OsonParser) { + OsonParser _parser = (OsonParser) parser; + + if(_parser.currentOsonEvent().equals(OracleJsonParser.Event.VALUE_DATE)) { + LocalDateTime dateTime = _parser.readLocalDateTime(); + return dateTime.toLocalDate(); + } + } + else { + return super.deserialize(parser, context); } + return super.deserialize(parser, context); + } } diff --git a/ojdbc-provider-jackson-oson/src/main/java/oracle/jdbc/provider/oson/deser/OsonLocalDateTimeDeserializer.java b/ojdbc-provider-jackson-oson/src/main/java/oracle/jdbc/provider/oson/deser/OsonLocalDateTimeDeserializer.java index 195c8666..bbd86063 100644 --- a/ojdbc-provider-jackson-oson/src/main/java/oracle/jdbc/provider/oson/deser/OsonLocalDateTimeDeserializer.java +++ b/ojdbc-provider-jackson-oson/src/main/java/oracle/jdbc/provider/oson/deser/OsonLocalDateTimeDeserializer.java @@ -40,7 +40,6 @@ import com.fasterxml.jackson.annotation.JsonFormat; import com.fasterxml.jackson.core.JsonParser; -import com.fasterxml.jackson.core.JsonTokenId; import com.fasterxml.jackson.databind.DeserializationContext; import com.fasterxml.jackson.databind.deser.std.StdScalarDeserializer; import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateTimeDeserializer; diff --git a/ojdbc-provider-jackson-oson/src/main/java/oracle/jdbc/provider/oson/deser/OsonSerializableDeserializer.java b/ojdbc-provider-jackson-oson/src/main/java/oracle/jdbc/provider/oson/deser/OsonSerializableDeserializer.java new file mode 100644 index 00000000..a0a55052 --- /dev/null +++ b/ojdbc-provider-jackson-oson/src/main/java/oracle/jdbc/provider/oson/deser/OsonSerializableDeserializer.java @@ -0,0 +1,90 @@ +/* + ** Copyright (c) 2024 Oracle and/or its affiliates. + ** + ** The Universal Permissive License (UPL), Version 1.0 + ** + ** Subject to the condition set forth below, permission is hereby granted to any + ** person obtaining a copy of this software, associated documentation and/or data + ** (collectively the "Software"), free of charge and under any and all copyright + ** rights in the Software, and any and all patent rights owned or freely + ** licensable by each licensor hereunder covering either (i) the unmodified + ** Software as contributed to or provided by such licensor, or (ii) the Larger + ** Works (as defined below), to deal in both + ** + ** (a) the Software, and + ** (b) any piece of software and/or hardware listed in the lrgrwrks.txt file if + ** one is included with the Software (each a "Larger Work" to which the Software + ** is contributed by such licensors), + ** + ** without restriction, including without limitation the rights to copy, create + ** derivative works of, display, perform, and distribute the Software and make, + ** use, sell, offer for sale, import, export, have made, and have sold the + ** Software and the Larger Work(s), and to sublicense the foregoing rights on + ** either these or other terms. + ** + ** This license is subject to the following condition: + ** The above copyright notice and either this complete permission notice or at + ** a minimum a reference to the UPL must be included in all copies or + ** substantial portions of the Software. + ** + ** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + ** IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + ** FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + ** AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + ** LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + ** OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + ** SOFTWARE. + */ +package oracle.jdbc.provider.oson.deser; + +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 java.io.ByteArrayInputStream; +import java.io.IOException; +import java.io.ObjectInputStream; +import java.io.Serializable; + +/** + * A custom deserializer for objects(Except from java.* package) implementing the `Serializable` interface. + * This deserializer reads binary data from the JSON input and reconstructs the object + * using Java's `ObjectInputStream`. + */ +public class OsonSerializableDeserializer extends JsonDeserializer { + /** + * Singleton instance for reuse. + */ + public static final OsonSerializableDeserializer INSTANCE = new OsonSerializableDeserializer(); + + /** + * Default constructor. + */ + OsonSerializableDeserializer() {} + + /** + * Deserializes binary data from JSON input into a `Serializable` object. + * + *

    The method reads binary data, interprets it as a serialized object, and reconstructs the + * object using an `ObjectInputStream`.

    + * + * @param p the JSON parser + * @param ctxt the deserialization context + * @return the deserialized `Serializable` object, or `null` if no data is present + * @throws IOException if an I/O error occurs during deserialization + * @throws JacksonException if a JSON parsing error occurs + */ + @Override + public Object deserialize(JsonParser p, DeserializationContext ctxt) throws IOException, JacksonException { + byte[] data = p.getBinaryValue(); + if (data == null) { + return null; + } + try (ObjectInputStream objectStream = new ObjectInputStream(new ByteArrayInputStream(data))) { + return (Serializable) objectStream.readObject(); // Deserialize the object + } catch (ClassNotFoundException e) { + throw new IOException("Class not found during deserialization", e); + } + } +} diff --git a/ojdbc-provider-jackson-oson/src/main/java/oracle/jdbc/provider/oson/deser/OsonSqlDateDeserializer.java b/ojdbc-provider-jackson-oson/src/main/java/oracle/jdbc/provider/oson/deser/OsonSqlDateDeserializer.java index bb070756..a15247fc 100644 --- a/ojdbc-provider-jackson-oson/src/main/java/oracle/jdbc/provider/oson/deser/OsonSqlDateDeserializer.java +++ b/ojdbc-provider-jackson-oson/src/main/java/oracle/jdbc/provider/oson/deser/OsonSqlDateDeserializer.java @@ -39,7 +39,6 @@ package oracle.jdbc.provider.oson.deser; import com.fasterxml.jackson.core.JsonParser; -import com.fasterxml.jackson.core.JsonTokenId; import com.fasterxml.jackson.databind.DeserializationContext; import com.fasterxml.jackson.databind.deser.std.DateDeserializers; import oracle.jdbc.provider.oson.OsonParser; @@ -49,33 +48,72 @@ import java.sql.Date; import java.text.DateFormat; import java.time.LocalDateTime; -import java.time.format.DateTimeFormatter; +/** + * A custom deserializer for SQL `Date` objects that extends Jackson's `SqlDateDeserializer`. + * This implementation is designed to handle Oracle JSON-specific events when using `OsonParser`, + * particularly the `VALUE_DATE` event. + * + *

    If the parser is an instance of `OsonParser` and encounters a `VALUE_DATE` event, the + * deserializer reads the date and converts it into an SQL `Date` object. For all other cases, + * it delegates to the base `SqlDateDeserializer`.

    + */ public class OsonSqlDateDeserializer extends DateDeserializers.SqlDateDeserializer { - public static final OsonSqlDateDeserializer INSTANCE = new OsonSqlDateDeserializer(); + /** + * Singleton instance for reuse. + */ + public static final OsonSqlDateDeserializer INSTANCE = new OsonSqlDateDeserializer(); - public OsonSqlDateDeserializer() { - super(); - } + /** + * Default constructor using the base deserializer's behavior. + */ + public OsonSqlDateDeserializer() { + super(); + } - public OsonSqlDateDeserializer(DateDeserializers.SqlDateDeserializer src, DateFormat df, String formatString) { - super(src, df, formatString); - } + /** + * Constructor with a custom date format. + * + * @param src the base `SqlDateDeserializer` instance + * @param df the `DateFormat` to use for deserialization + * @param formatString the format string for the date + */ + public OsonSqlDateDeserializer(DateDeserializers.SqlDateDeserializer src, DateFormat df, String formatString) { + super(src, df, formatString); + } - @Override - protected DateDeserializers.SqlDateDeserializer withDateFormat(DateFormat df, String formatString) { - return super.withDateFormat(df, formatString); - } + /** + * Creates a new deserializer with the specified date format. + * + * @param df the `DateFormat` to use + * @param formatString the format string for the date + * @return a new `SqlDateDeserializer` with the given format + */ + @Override + protected DateDeserializers.SqlDateDeserializer withDateFormat(DateFormat df, String formatString) { + return super.withDateFormat(df, formatString); + } - @Override - public Date deserialize(JsonParser p, DeserializationContext ctxt) throws IOException { - if( p instanceof OsonParser) { - OsonParser parser = (OsonParser) p; - if(parser.currentOsonEvent().equals(OracleJsonParser.Event.VALUE_DATE)) { - LocalDateTime dateTime = parser.getLocalDateTime(); - return Date.valueOf(dateTime.toLocalDate()); - } - } - return super.deserialize(p, ctxt); + /** + * Deserializes a JSON input into an SQL `Date` object. + * + *

    Handles `OsonParser`-specific `VALUE_DATE` events to directly parse the date into an + * SQL `Date` object. For all other scenarios, the base deserialization behavior is used.

    + * + * @param p the JSON parser + * @param ctxt the deserialization context + * @return the deserialized SQL `Date` object + * @throws IOException if an I/O error occurs during deserialization + */ + @Override + public Date deserialize(JsonParser p, DeserializationContext ctxt) throws IOException { + if( p instanceof OsonParser) { + OsonParser parser = (OsonParser) p; + if(parser.currentOsonEvent().equals(OracleJsonParser.Event.VALUE_DATE)) { + LocalDateTime dateTime = parser.readLocalDateTime(); + return Date.valueOf(dateTime.toLocalDate()); + } } + return super.deserialize(p, ctxt); + } } diff --git a/ojdbc-provider-jackson-oson/src/main/java/oracle/jdbc/provider/oson/deser/OsonTimeStampDeserializer.java b/ojdbc-provider-jackson-oson/src/main/java/oracle/jdbc/provider/oson/deser/OsonTimeStampDeserializer.java index e9726d43..0e8e1a88 100644 --- a/ojdbc-provider-jackson-oson/src/main/java/oracle/jdbc/provider/oson/deser/OsonTimeStampDeserializer.java +++ b/ojdbc-provider-jackson-oson/src/main/java/oracle/jdbc/provider/oson/deser/OsonTimeStampDeserializer.java @@ -45,44 +45,92 @@ import oracle.sql.json.OracleJsonParser; import java.io.IOException; -import java.sql.Date; import java.sql.Timestamp; import java.text.DateFormat; import java.time.LocalDateTime; +/** + * A custom deserializer for `Timestamp` objects that extends Jackson's `TimestampDeserializer`. + * This implementation is tailored to work with `OsonParser` and handle Oracle JSON-specific + * events such as `VALUE_TIMESTAMP`. + * + *

    For standard JSON input, it delegates to the base `TimestampDeserializer`. When using + * `OsonParser` and encountering a `VALUE_TIMESTAMP` event, it converts the timestamp into a + * `Timestamp` object directly.

    + */ public class OsonTimeStampDeserializer extends DateDeserializers.TimestampDeserializer { - public final static OsonTimeStampDeserializer INSTANCE = new OsonTimeStampDeserializer(); + /** + * Singleton instance for reuse. + */ + public final static OsonTimeStampDeserializer INSTANCE = new OsonTimeStampDeserializer(); - public OsonTimeStampDeserializer() { - super(); - } + /** + * Default constructor using the base deserializer's behavior. + */ + public OsonTimeStampDeserializer() { + super(); + } - public OsonTimeStampDeserializer(DateDeserializers.TimestampDeserializer src, DateFormat df, String formatString) { - super(src, df, formatString); - } + /** + * Constructor with a custom date format. + * + * @param src the base `TimestampDeserializer` instance + * @param df the `DateFormat` to use for deserialization + * @param formatString the format string for the timestamp + */ + public OsonTimeStampDeserializer(DateDeserializers.TimestampDeserializer src, + DateFormat df, String formatString) { + super(src, df, formatString); + } - @Override - protected DateDeserializers.TimestampDeserializer withDateFormat(DateFormat df, String formatString) { - return super.withDateFormat(df, formatString); - } + /** + * Creates a new deserializer with the specified date format. + * + * @param df the `DateFormat` to use + * @param formatString the format string for the timestamp + * @return a new `TimestampDeserializer` with the given format + */ + @Override + protected OsonTimeStampDeserializer withDateFormat(DateFormat df, String formatString) { + return new OsonTimeStampDeserializer(this, df, formatString); + } - @Override - public Object getEmptyValue(DeserializationContext ctxt) { - return super.getEmptyValue(ctxt); - } - - @Override - public Timestamp deserialize(JsonParser p, DeserializationContext ctxt) throws IOException { - // custom format - if( p instanceof OsonParser) { - OsonParser parser = (OsonParser) p; - if(parser.currentOsonEvent().equals(OracleJsonParser.Event.VALUE_TIMESTAMP)) { - LocalDateTime dateTime = parser.getLocalDateTime(); - return Timestamp.valueOf(dateTime); + /** + * Returns an empty value for the deserializer, delegating to the base implementation. + * + * @param ctxt the deserialization context + * @return the empty value + */ + @Override + public Object getEmptyValue(DeserializationContext ctxt) { + return super.getEmptyValue(ctxt); + } - } - } + /** + * Deserializes a JSON input into a `Timestamp` object. + * + *

    Handles `OsonParser`-specific `VALUE_TIMESTAMP` events to directly parse the timestamp + * into a `Timestamp` object. For all other scenarios, it falls back to the base deserialization + * behavior.

    + * + * @param p the JSON parser + * @param ctxt the deserialization context + * @return the deserialized `Timestamp` object + * @throws IOException if an I/O error occurs during deserialization + */ + @Override + public Timestamp deserialize(JsonParser p, DeserializationContext ctxt) throws IOException { + if (_customFormat != null){ + return super.deserialize(p, ctxt); + } + if( p instanceof OsonParser) { + OsonParser parser = (OsonParser) p; + if(parser.currentOsonEvent().equals(OracleJsonParser.Event.VALUE_TIMESTAMP)) { + LocalDateTime dateTime = parser.readLocalDateTime(); + return Timestamp.valueOf(dateTime); - return super.deserialize(p, ctxt); + } } + return super.deserialize(p, ctxt); + } } diff --git a/ojdbc-provider-jackson-oson/src/main/java/oracle/jdbc/provider/oson/deser/OsonUUIDDeserializer.java b/ojdbc-provider-jackson-oson/src/main/java/oracle/jdbc/provider/oson/deser/OsonUUIDDeserializer.java index 8fde8194..4db8f19f 100644 --- a/ojdbc-provider-jackson-oson/src/main/java/oracle/jdbc/provider/oson/deser/OsonUUIDDeserializer.java +++ b/ojdbc-provider-jackson-oson/src/main/java/oracle/jdbc/provider/oson/deser/OsonUUIDDeserializer.java @@ -40,6 +40,7 @@ import com.fasterxml.jackson.core.JacksonException; import com.fasterxml.jackson.core.JsonParser; +import com.fasterxml.jackson.core.JsonToken; import com.fasterxml.jackson.databind.DeserializationContext; import com.fasterxml.jackson.databind.deser.std.StdScalarDeserializer; import com.fasterxml.jackson.databind.deser.std.UUIDDeserializer; @@ -49,24 +50,57 @@ import java.nio.ByteBuffer; import java.util.UUID; +/** + * A custom deserializer for `UUID` objects that extends Jackson's `StdScalarDeserializer`. + * This deserializer is designed to handle Oracle JSON-specific binary UUID representations + * when used with `OsonParser`. + * + *

    When encountering an embedded object token (`JsonToken.VALUE_EMBEDDED_OBJECT`) in + * `OsonParser`, it reads the binary data, extracts the most significant and least significant + * bits, and constructs a `UUID` object. For other scenarios, it delegates to Jackson's + * `UUIDDeserializer`.

    + */ public class OsonUUIDDeserializer extends StdScalarDeserializer { - public static final OsonUUIDDeserializer INSTANCE = new OsonUUIDDeserializer(); - public static final UUIDDeserializer UUID_DESERIALIZER = new UUIDDeserializer(); + /** + * Singleton instance for reuse. + */ + public static final OsonUUIDDeserializer INSTANCE = new OsonUUIDDeserializer(); + /** + * Fallback Jackson `UUIDDeserializer` for standard cases. + */ + public static final UUIDDeserializer UUID_DESERIALIZER = new UUIDDeserializer(); - public OsonUUIDDeserializer() { - super(UUID.class); - } + /** + * Default constructor. + */ + public OsonUUIDDeserializer() { + super(UUID.class); + } - @Override - public UUID deserialize(JsonParser p, DeserializationContext ctxt) throws IOException, JacksonException { - if (p instanceof OsonParser) { - byte[] bytes = p.getBinaryValue(); - ByteBuffer byteBuffer = ByteBuffer.wrap(bytes); - long mostSignificantBits = byteBuffer.getLong(); - long leastSignificantBits = byteBuffer.getLong(); - return new UUID(mostSignificantBits, leastSignificantBits); - } else { - return UUID_DESERIALIZER.deserialize(p,ctxt); - } + /** + * Deserializes a JSON input into a `UUID` object. + * + *

    If the parser is an instance of `OsonParser` and the current token is an embedded + * object (`JsonToken.VALUE_EMBEDDED_OBJECT`), it interprets the binary data as a + * serialized UUID. For all other scenarios, it delegates to the standard + * `UUIDDeserializer`.

    + * + * @param p the JSON parser + * @param ctxt the deserialization context + * @return the deserialized `UUID` object + * @throws IOException if an I/O error occurs during deserialization + */ + @Override + public UUID deserialize(JsonParser p, DeserializationContext ctxt) throws IOException{ + if (p instanceof OsonParser + && p.currentToken().equals(JsonToken.VALUE_EMBEDDED_OBJECT)) { + byte[] bytes = p.getBinaryValue(); + ByteBuffer byteBuffer = ByteBuffer.wrap(bytes); + long mostSignificantBits = byteBuffer.getLong(); + long leastSignificantBits = byteBuffer.getLong(); + return new UUID(mostSignificantBits, leastSignificantBits); + } else { + return UUID_DESERIALIZER.deserialize(p,ctxt); } + } } diff --git a/ojdbc-provider-jackson-oson/src/main/java/oracle/jdbc/provider/oson/deser/OsosnSerializableDeserializer.java b/ojdbc-provider-jackson-oson/src/main/java/oracle/jdbc/provider/oson/deser/OsosnSerializableDeserializer.java deleted file mode 100644 index c31cba5b..00000000 --- a/ojdbc-provider-jackson-oson/src/main/java/oracle/jdbc/provider/oson/deser/OsosnSerializableDeserializer.java +++ /dev/null @@ -1,30 +0,0 @@ -package oracle.jdbc.provider.oson.deser; - -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 java.io.ByteArrayInputStream; -import java.io.IOException; -import java.io.ObjectInputStream; -import java.io.Serializable; - -public class OsosnSerializableDeserializer extends JsonDeserializer { - public static final OsosnSerializableDeserializer INSTANCE = new OsosnSerializableDeserializer(); - - OsosnSerializableDeserializer() {} - - @Override - public Object deserialize(JsonParser p, DeserializationContext ctxt) throws IOException, JacksonException { - byte[] data = p.getBinaryValue(); - if (data == null) { - return null; - } - try (ObjectInputStream objectStream = new ObjectInputStream(new ByteArrayInputStream(data))) { - return (Serializable) objectStream.readObject(); // Deserialize the object - } catch (ClassNotFoundException e) { - throw new IOException("Class not found during deserialization", e); - } - } -} diff --git a/ojdbc-provider-jackson-oson/src/main/java/oracle/jdbc/provider/oson/ser/OsonConverterArraySerializer.java b/ojdbc-provider-jackson-oson/src/main/java/oracle/jdbc/provider/oson/ser/OsonConverterArraySerializer.java index b5dacb0c..9acfb130 100644 --- a/ojdbc-provider-jackson-oson/src/main/java/oracle/jdbc/provider/oson/ser/OsonConverterArraySerializer.java +++ b/ojdbc-provider-jackson-oson/src/main/java/oracle/jdbc/provider/oson/ser/OsonConverterArraySerializer.java @@ -51,72 +51,96 @@ import java.io.IOException; import java.lang.reflect.InvocationTargetException; +/** + * A custom serializer for arrays of objects, using an `AttributeConverter` to convert each element + * of the array into its database-compatible representation before serializing it to JSON. + * This serializer is an extension of Jackson's `ArraySerializerBase`. + */ public class OsonConverterArraySerializer extends ArraySerializerBase { - private AttributeConverter attributeConverter; - - public OsonConverterArraySerializer(Class converter) { - super(Object[].class); - try { - this.attributeConverter = converter.getConstructor().newInstance(); - } catch (InstantiationException - | IllegalAccessException - | InvocationTargetException - | NoSuchMethodException e) { - throw new RuntimeException(e); - } - } - - @Override - public void serialize(Object[] value, JsonGenerator gen, SerializerProvider provider) throws IOException { - int len = value.length; -// if (len == 1) { -// Object convertedValue = attributeConverter.convertToDatabaseColumn(value[0]); -// gen.writeObject(convertedValue); -// return; -// } - gen.writeStartArray(); - - for (int i = 0; i < len; i++) { - if (value[i] == null) { - gen.writeNull(); - }else { - Object convertedValue = attributeConverter.convertToDatabaseColumn(value[i]); - gen.writeObject(convertedValue); - } - } - gen.writeEndArray(); - } - - - @Override - public JsonSerializer _withResolved(BeanProperty prop, Boolean unwrapSingle) { - return null; - } - - @Override - protected void serializeContents(Object[] value, JsonGenerator jgen, SerializerProvider provider){ - serializeContents(value, jgen, provider); + /** + * The attribute converter used to transform array elements during serialization. + */ + private final AttributeConverter attributeConverter; + + /** + * Constructs an instance of `OsonConverterArraySerializer` with the provided `AttributeConverter` class. + * + * @param converter the class of the `AttributeConverter` to use + * @throws RuntimeException if the converter cannot be instantiated + */ + public OsonConverterArraySerializer(Class converter) { + super(Object[].class); + try { + this.attributeConverter = converter.getConstructor().newInstance(); + } catch (InstantiationException + | IllegalAccessException + | InvocationTargetException + | NoSuchMethodException e) { + throw new RuntimeException(e); } - - @Override - public JavaType getContentType() { - return null; - } - - @Override - public JsonSerializer getContentSerializer() { - return null; - } - - @Override - public boolean hasSingleElement(Object[] value) { - return false; - } - - @Override - protected ContainerSerializer _withValueTypeSerializer(TypeSerializer vts) { - return null; + } + + /** + * Serializes the array of objects by converting each element using the `AttributeConverter` + * and writing the resulting values as JSON array. + * + * @param value the array of objects to serialize + * @param gen the JSON generator used to write the JSON output + * @param provider the serializer provider + * @throws IOException if an I/O error occurs during serialization + */ + @Override + public void serialize(Object[] value, JsonGenerator gen, SerializerProvider provider) throws IOException { + int len = value.length; +// if (len == 1) { +// Object convertedValue = attributeConverter.convertToDatabaseColumn(value[0]); +// gen.writeObject(convertedValue); +// return; +// } + gen.writeStartArray(); + + for (int i = 0; i < len; i++) { + if (value[i] == null) { + gen.writeNull(); + }else { + Object convertedValue = attributeConverter.convertToDatabaseColumn(value[i]); + gen.writeObject(convertedValue); + } } + gen.writeEndArray(); + } + + // The following methods are inherited but are not used in this serializer. + + @Override + public JsonSerializer _withResolved(BeanProperty prop, Boolean unwrapSingle) { + return null; + } + + @Override + protected void serializeContents(Object[] value, JsonGenerator jgen, SerializerProvider provider){ + // no-op + } + + @Override + public JavaType getContentType() { + return null; + } + + @Override + public JsonSerializer getContentSerializer() { + return null; + } + + @Override + public boolean hasSingleElement(Object[] value) { + return false; + } + + @Override + protected ContainerSerializer _withValueTypeSerializer(TypeSerializer vts) { + return null; + } } diff --git a/ojdbc-provider-jackson-oson/src/main/java/oracle/jdbc/provider/oson/ser/OsonConverterSerialiser.java b/ojdbc-provider-jackson-oson/src/main/java/oracle/jdbc/provider/oson/ser/OsonConverterSerializer.java similarity index 56% rename from ojdbc-provider-jackson-oson/src/main/java/oracle/jdbc/provider/oson/ser/OsonConverterSerialiser.java rename to ojdbc-provider-jackson-oson/src/main/java/oracle/jdbc/provider/oson/ser/OsonConverterSerializer.java index 87e2f331..b804f871 100644 --- a/ojdbc-provider-jackson-oson/src/main/java/oracle/jdbc/provider/oson/ser/OsonConverterSerialiser.java +++ b/ojdbc-provider-jackson-oson/src/main/java/oracle/jdbc/provider/oson/ser/OsonConverterSerializer.java @@ -46,29 +46,52 @@ import java.io.IOException; import java.lang.reflect.InvocationTargetException; -public class OsonConverterSerialiser extends JsonSerializer { +/** + * A custom serializer for converting Java objects using an `AttributeConverter` during serialization. + * This serializer leverages JPA-style `AttributeConverter` to convert an object into its + * database-compatible representation before writing it to JSON. + */ +public class OsonConverterSerializer extends JsonSerializer { - private AttributeConverter attributeConverter; + /** + * The attribute converter used to transform objects during serialization. + */ + private AttributeConverter attributeConverter; - public OsonConverterSerialiser(Class converter) { - try { - this.attributeConverter = converter.getConstructor().newInstance(); - } catch (InstantiationException - | IllegalAccessException - | InvocationTargetException - | NoSuchMethodException e) { - throw new RuntimeException(e); - } + /** + * Constructs an instance of `OsonConverterSerializer` with the provided `AttributeConverter` class. + * + * @param converter the class of the `AttributeConverter` to use + * @throws RuntimeException if the converter cannot be instantiated + */ + public OsonConverterSerializer(Class converter) { + try { + this.attributeConverter = converter.getConstructor().newInstance(); + } catch (InstantiationException + | IllegalAccessException + | InvocationTargetException + | NoSuchMethodException e) { + throw new RuntimeException(e); } + } - @Override - public void serialize(Object value, JsonGenerator gen, SerializerProvider serializers) throws IOException { - if (value == null) { - gen.writeNull(); - return; - } - - Object convertedValue = attributeConverter.convertToDatabaseColumn(value); - gen.writeObject(convertedValue); + /** + * Serializes a given value by converting it with the `AttributeConverter` and writing the result + * to the JSON generator. + * + * @param value the object to serialize + * @param gen the JSON generator used to write the JSON output + * @param serializers the serializer provider + * @throws IOException if an I/O error occurs during serialization + */ + @Override + public void serialize(Object value, JsonGenerator gen, SerializerProvider serializers) throws IOException { + if (value == null) { + gen.writeNull(); + return; } + + Object convertedValue = attributeConverter.convertToDatabaseColumn(value); + gen.writeObject(convertedValue); + } } diff --git a/ojdbc-provider-jackson-oson/src/main/java/oracle/jdbc/provider/oson/ser/OsonDateSerializer.java b/ojdbc-provider-jackson-oson/src/main/java/oracle/jdbc/provider/oson/ser/OsonDateSerializer.java index 4ec31402..2338a2bf 100644 --- a/ojdbc-provider-jackson-oson/src/main/java/oracle/jdbc/provider/oson/ser/OsonDateSerializer.java +++ b/ojdbc-provider-jackson-oson/src/main/java/oracle/jdbc/provider/oson/ser/OsonDateSerializer.java @@ -66,102 +66,105 @@ */ public class OsonDateSerializer extends DateSerializer { - /** - * Singleton instance of the OsonDateSerializer with default configuration. - */ - public static final OsonDateSerializer INSTANCE = new OsonDateSerializer(false,false); + /** + * Singleton instance of the OsonDateSerializer with default configuration. + */ + public static final OsonDateSerializer INSTANCE = + new OsonDateSerializer(false,false); - /** - * Indicates whether the serializer should handle temporal timestamps. - */ - private boolean isTemporalTimeStamp = false; + /** + * Indicates whether the serializer should handle temporal timestamps. + */ + private boolean isTemporalTimeStamp = false; - /** - * Indicates whether the serializer should handle temporal time objects. - */ + /** + * Indicates whether the serializer should handle temporal time objects. + */ - private boolean isTemporalTime = false; + private boolean isTemporalTime = false; - @Override - public OsonDateSerializer withFormat(Boolean timestamp, DateFormat customFormat) { - return new OsonDateSerializer(timestamp,customFormat); - } + @Override + public OsonDateSerializer withFormat(Boolean timestamp, DateFormat customFormat) { + return new OsonDateSerializer(timestamp,customFormat); + } - /** - * Constructs a serializer with specific handling for temporal timestamps or time objects. - * - * @param isTemporalTimeStamp whether to handle temporal timestamps. - * @param isTemporalTime whether to handle temporal time objects. - */ - public OsonDateSerializer(boolean isTemporalTimeStamp, boolean isTemporalTime) { - this(null,null); - this.isTemporalTimeStamp = isTemporalTimeStamp; - this.isTemporalTime = isTemporalTime; - } + /** + * Constructs a serializer with specific handling for temporal timestamps or time objects. + * + * @param isTemporalTimeStamp whether to handle temporal timestamps. + * @param isTemporalTime whether to handle temporal time objects. + */ + public OsonDateSerializer(boolean isTemporalTimeStamp, boolean isTemporalTime) { + this(null,null); + this.isTemporalTimeStamp = isTemporalTimeStamp; + this.isTemporalTime = isTemporalTime; + } - /** - * Constructs a serializer with a custom timestamp usage and date format. - * - * @param useTimestamp whether to use timestamps for serialization. - * @param customFormat the custom date format for serialization. - */ - public OsonDateSerializer(Boolean useTimestamp, DateFormat customFormat) { - super(useTimestamp, customFormat); - } + /** + * Constructs a serializer with a custom timestamp usage and date format. + * + * @param useTimestamp whether to use timestamps for serialization. + * @param customFormat the custom date format for serialization. + */ + public OsonDateSerializer(Boolean useTimestamp, DateFormat customFormat) { + super(useTimestamp, customFormat); + } - /** - * Serializes a `Date` object into JSON, handling special cases for timestamps and time objects. - * - * @param value the date value to serialize. - * @param g the JSON generator. - * @param provider the serializer provider. - * @throws IOException if an I/O error occurs during serialization. - */ - @Override - public void serialize(Date value, JsonGenerator g, SerializerProvider provider) throws IOException { - if(_customFormat != null) { - super.serialize(value, g, provider); - return; - } - if( value instanceof java.sql.Time) { - g.writeString(value.toString()); - return; - } - if (g instanceof OsonGenerator) { - if(value instanceof Timestamp) { - ((OsonGenerator)g).writeTimeStamp((Timestamp) value); - return; - } - ((OsonGenerator)g).writeDate(value); - return; - } - super.serialize(value, g, provider); + /** + * Serializes a `Date` object into JSON, handling special cases for timestamps and time objects. + * + * @param value the date value to serialize. + * @param g the JSON generator. + * @param provider the serializer provider. + * @throws IOException if an I/O error occurs during serialization. + */ + @Override + public void serialize(Date value, JsonGenerator g, SerializerProvider provider) + throws IOException { + if(_customFormat != null) { + super.serialize(value, g, provider); + return; + } + if( value instanceof java.sql.Time) { + g.writeString(value.toString()); + return; + } + if (g instanceof OsonGenerator) { + if(value instanceof Timestamp) { + ((OsonGenerator)g).writeTimeStamp((Timestamp) value); + return; + } + ((OsonGenerator)g).writeDate(value); + return; } + super.serialize(value, g, provider); + } - /** - * Creates a contextual serializer based on the `@Temporal` annotation. - * - * @param serializers the serializer provider. - * @param property the bean property being serialized. - * @return a contextual instance of `OsonDateSerializer`. - * @throws JsonMappingException if contextual serialization fails. - */ - @Override - public JsonSerializer createContextual(SerializerProvider serializers, BeanProperty property) throws JsonMappingException { - if (property != null) { - AnnotatedMember introspector = property.getMember(); - if (introspector != null) { - Temporal temporal = introspector.getAnnotation(Temporal.class); - if (temporal != null) { - if (temporal.value() == TemporalType.TIMESTAMP) { - return new OsonDateSerializer(true,false); - } - if (temporal.value() == TemporalType.TIME) { - return new OsonDateSerializer(false,true); - } - } - } + /** + * Creates a contextual serializer based on the `@Temporal` annotation. + * + * @param serializers the serializer provider. + * @param property the bean property being serialized. + * @return a contextual instance of `OsonDateSerializer`. + * @throws JsonMappingException if contextual serialization fails. + */ + @Override + public JsonSerializer createContextual(SerializerProvider serializers, + BeanProperty property) throws JsonMappingException { + if (property != null) { + AnnotatedMember introspector = property.getMember(); + if (introspector != null) { + Temporal temporal = introspector.getAnnotation(Temporal.class); + if (temporal != null) { + if (temporal.value() == TemporalType.TIMESTAMP) { + return new OsonDateSerializer(true,false); + } + if (temporal.value() == TemporalType.TIME) { + return new OsonDateSerializer(false,true); + } } - return super.createContextual(serializers, property); + } } + return super.createContextual(serializers, property); + } } diff --git a/ojdbc-provider-jackson-oson/src/main/java/oracle/jdbc/provider/oson/ser/OsonEnumSerializer.java b/ojdbc-provider-jackson-oson/src/main/java/oracle/jdbc/provider/oson/ser/OsonEnumSerializer.java index 8cc64024..489c3706 100644 --- a/ojdbc-provider-jackson-oson/src/main/java/oracle/jdbc/provider/oson/ser/OsonEnumSerializer.java +++ b/ojdbc-provider-jackson-oson/src/main/java/oracle/jdbc/provider/oson/ser/OsonEnumSerializer.java @@ -52,47 +52,91 @@ import java.io.IOException; import java.lang.reflect.Field; +/** + * A custom serializer for Java `Enum` types, allowing them to be serialized as either their + * ordinal value or their name (as a string), based on annotations or provided flags. + * This serializer supports both the default ordinal-based serialization and string-based serialization + * for enumerated types. + */ public class OsonEnumSerializer extends JsonSerializer> implements ContextualSerializer { + /** + * Flag indicating if the enum should be serialized as its ordinal value or name. + * When true, the enum is serialized as a number (ordinal). + */ + private final boolean isEnumerated; - private boolean isEnumerated; - private boolean isEnumeratedString; - public OsonEnumSerializer(boolean isEnumerated, boolean isEnumeratedString) { - this.isEnumerated = isEnumerated; - this.isEnumeratedString = isEnumeratedString; - } - public OsonEnumSerializer() { - this(false,false); - } + /** + * Flag indicating if the enum should be serialized as its name (string) instead of its ordinal. + */ + private final boolean isEnumeratedString; - @Override - public void serialize(Enum value, JsonGenerator gen, SerializerProvider serializers) throws IOException { - if (value == null) { - gen.writeNull(); - return; - } - if (isEnumerated && isEnumeratedString) { - gen.writeString(value.name()); - return; - } - gen.writeNumber(value.ordinal()); + /** + * Constructs an `OsonEnumSerializer` with specific flags for enum serialization. + * + * @param isEnumerated whether the enum should be serialized as its ordinal (if true) + * @param isEnumeratedString whether the enum should be serialized as its name (if true) + */ + public OsonEnumSerializer(boolean isEnumerated, boolean isEnumeratedString) { + this.isEnumerated = isEnumerated; + this.isEnumeratedString = isEnumeratedString; + } + + /** + * Default constructor, which assumes the enum should not be serialized as either ordinal or string. + */ + public OsonEnumSerializer() { + this(false,false); + } + + /** + * Serializes an enum based on the configured flags: + * - If both `isEnumerated` and `isEnumeratedString` are true, serialize the enum as a string (name). + * - If only `isEnumerated` is true, serialize the enum as its ordinal (number). + * - Otherwise, the enum is serialized by its ordinal value. + * + * @param value the enum value to serialize + * @param gen the JSON generator + * @param serializers the serializer provider + * @throws IOException if an I/O error occurs during serialization + */ + @Override + public void serialize(Enum value, JsonGenerator gen, SerializerProvider serializers) throws IOException { + if (value == null) { + gen.writeNull(); + return; + } + if (isEnumerated && isEnumeratedString) { + gen.writeString(value.name()); + return; } + gen.writeNumber(value.ordinal()); + } - @Override - public JsonSerializer createContextual(SerializerProvider prov, BeanProperty property) throws JsonMappingException { - boolean isEnumeratedCT = false, isEnumeratedStringCT = false; - if(property != null) { - AnnotatedMember member = property.getMember(); - if (member != null){ - Enumerated enumerated = member.getAnnotation(Enumerated.class); - if (enumerated != null){ - isEnumeratedCT = true; - if (enumerated.value() == EnumType.STRING) { - isEnumeratedStringCT = true; - } - return new OsonEnumSerializer(isEnumeratedCT, isEnumeratedStringCT); - } - } + /** + * Contextually configures the enum serializer based on annotations present in the property. + * Specifically, it checks for the `@Enumerated` annotation to determine whether the enum should + * be serialized as a string (name) or as its ordinal. + * + * @param prov the serializer provider + * @param property the bean property being serialized + * @return a contextualized enum serializer + */ + @Override + public JsonSerializer createContextual(SerializerProvider prov, BeanProperty property) { + boolean isEnumeratedCT = false, isEnumeratedStringCT = false; + if(property != null) { + AnnotatedMember member = property.getMember(); + if (member != null){ + Enumerated enumerated = member.getAnnotation(Enumerated.class); + if (enumerated != null){ + isEnumeratedCT = true; + if (enumerated.value() == EnumType.STRING) { + isEnumeratedStringCT = true; + } + return new OsonEnumSerializer(isEnumeratedCT, isEnumeratedStringCT); } - return new OsonEnumSerializer(false,false); + } } + return new OsonEnumSerializer(false,false); + } } diff --git a/ojdbc-provider-jackson-oson/src/main/java/oracle/jdbc/provider/oson/ser/OsonLocalDateSerializer.java b/ojdbc-provider-jackson-oson/src/main/java/oracle/jdbc/provider/oson/ser/OsonLocalDateSerializer.java index e3afd456..93056eac 100644 --- a/ojdbc-provider-jackson-oson/src/main/java/oracle/jdbc/provider/oson/ser/OsonLocalDateSerializer.java +++ b/ojdbc-provider-jackson-oson/src/main/java/oracle/jdbc/provider/oson/ser/OsonLocalDateSerializer.java @@ -1,3 +1,40 @@ +/* + ** Copyright (c) 2024 Oracle and/or its affiliates. + ** + ** The Universal Permissive License (UPL), Version 1.0 + ** + ** Subject to the condition set forth below, permission is hereby granted to any + ** person obtaining a copy of this software, associated documentation and/or data + ** (collectively the "Software"), free of charge and under any and all copyright + ** rights in the Software, and any and all patent rights owned or freely + ** licensable by each licensor hereunder covering either (i) the unmodified + ** Software as contributed to or provided by such licensor, or (ii) the Larger + ** Works (as defined below), to deal in both + ** + ** (a) the Software, and + ** (b) any piece of software and/or hardware listed in the lrgrwrks.txt file if + ** one is included with the Software (each a "Larger Work" to which the Software + ** is contributed by such licensors), + ** + ** without restriction, including without limitation the rights to copy, create + ** derivative works of, display, perform, and distribute the Software and make, + ** use, sell, offer for sale, import, export, have made, and have sold the + ** Software and the Larger Work(s), and to sublicense the foregoing rights on + ** either these or other terms. + ** + ** This license is subject to the following condition: + ** The above copyright notice and either this complete permission notice or at + ** a minimum a reference to the UPL must be included in all copies or + ** substantial portions of the Software. + ** + ** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + ** IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + ** FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + ** AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + ** LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + ** OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + ** SOFTWARE. + */ package oracle.jdbc.provider.oson.ser; import com.fasterxml.jackson.annotation.JsonFormat; @@ -10,40 +47,83 @@ import java.time.LocalDate; import java.time.format.DateTimeFormatter; +/** + * A custom serializer for `LocalDate` types, extending `LocalDateSerializer` to add custom serialization behavior. + * This serializer is designed to handle `LocalDate` objects either using a provided `DateTimeFormatter` or a custom method + * to write `LocalDate` values in a specific format. + */ public class OsonLocalDateSerializer extends LocalDateSerializer { - public static final OsonLocalDateSerializer INSTANCE = new OsonLocalDateSerializer(); + /** + * A singleton instance of the `OsonLocalDateSerializer` for global use. + */ + public static final OsonLocalDateSerializer INSTANCE = new OsonLocalDateSerializer(); - public OsonLocalDateSerializer() { - super(); - } + /** + * Default constructor that initializes the serializer with default settings. + */ + public OsonLocalDateSerializer() { + super(); + } - public OsonLocalDateSerializer(OsonLocalDateSerializer base, Boolean useTimestamp, - DateTimeFormatter dtf, JsonFormat.Shape shape) { - super(base, useTimestamp, dtf, shape); - } + /** + * Constructor that allows customizing the serializer with specific settings such as timestamp usage, + * date format, and the desired shape of the output (e.g., as a string or as a numeric timestamp). + * + * @param base the base serializer to copy settings from + * @param useTimestamp whether to serialize as a timestamp + * @param dtf the custom date-time formatter to use for serialization + * @param shape the desired output shape (e.g., string or numeric) + */ + public OsonLocalDateSerializer(OsonLocalDateSerializer base, Boolean useTimestamp, + DateTimeFormatter dtf, JsonFormat.Shape shape) { + super(base, useTimestamp, dtf, shape); + } - public OsonLocalDateSerializer(DateTimeFormatter formatter) { - super(formatter); - } + /** + * Constructor that uses a custom date-time formatter for serialization. + * + * @param formatter the custom date-time formatter to use + */ + public OsonLocalDateSerializer(DateTimeFormatter formatter) { + super(formatter); + } - @Override - protected OsonLocalDateSerializer withFormat(Boolean useTimestamp, DateTimeFormatter dtf, JsonFormat.Shape shape) { - return new OsonLocalDateSerializer(this, useTimestamp, dtf, shape); - } + /** + * Creates a new instance of the `OsonLocalDateSerializer` with the specified settings. + * This method is typically used when the format or shape of the output needs to be adjusted. + * + * @param useTimestamp whether to serialize as a timestamp + * @param dtf the custom date-time formatter + * @param shape the desired output shape (e.g., string or numeric) + * @return a new instance of `OsonLocalDateSerializer` + */ + @Override + protected OsonLocalDateSerializer withFormat(Boolean useTimestamp, DateTimeFormatter dtf, JsonFormat.Shape shape) { + return new OsonLocalDateSerializer(this, useTimestamp, dtf, shape); + } - @Override - public void serialize(LocalDate date, JsonGenerator g, SerializerProvider provider) throws IOException { - if(_formatter != null || _shape != null) { - super.serialize(date, g, provider); - return; - } - if (g instanceof OsonGenerator) { - OsonGenerator generator = (OsonGenerator) g; - generator.writeLocalDate(date); - return; - }else { - super.serialize(date, g, provider); - } + /** + * Serializes a `LocalDate` object to JSON. + * If custom formatting or shape is specified, it uses the parent class (`LocalDateSerializer`) to perform the serialization. + * If no custom format is provided, it writes the `LocalDate` using the `OsonGenerator`'s `writeLocalDate` method. + * + * @param date the `LocalDate` object to serialize + * @param g the JSON generator used for serialization + * @param provider the serializer provider + * @throws IOException if an I/O error occurs during serialization + */ + @Override + public void serialize(LocalDate date, JsonGenerator g, SerializerProvider provider) throws IOException { + if(_formatter != null || _shape != null) { + super.serialize(date, g, provider); + return; + } + if (g instanceof OsonGenerator) { + OsonGenerator generator = (OsonGenerator) g; + generator.writeLocalDate(date); + }else { + super.serialize(date, g, provider); } + } } diff --git a/ojdbc-provider-jackson-oson/src/main/java/oracle/jdbc/provider/oson/ser/OsonLocalDateTimeSerializer.java b/ojdbc-provider-jackson-oson/src/main/java/oracle/jdbc/provider/oson/ser/OsonLocalDateTimeSerializer.java index ab36e942..2f5057e2 100644 --- a/ojdbc-provider-jackson-oson/src/main/java/oracle/jdbc/provider/oson/ser/OsonLocalDateTimeSerializer.java +++ b/ojdbc-provider-jackson-oson/src/main/java/oracle/jdbc/provider/oson/ser/OsonLocalDateTimeSerializer.java @@ -106,6 +106,10 @@ protected DateTimeFormatter _defaultFormatter() { */ @Override public void serialize(LocalDateTime value, JsonGenerator gen, SerializerProvider provider) throws IOException { + if(_formatter!= null || _shape!= null ) { + super.serialize(value, gen, provider); + return; + } if (gen instanceof OsonGenerator) { ((OsonGenerator) gen).writeLocalDateTime(value); diff --git a/ojdbc-provider-jackson-oson/src/main/java/oracle/jdbc/provider/oson/ser/OsonSerializableSerializer.java b/ojdbc-provider-jackson-oson/src/main/java/oracle/jdbc/provider/oson/ser/OsonSerializableSerializer.java index 97bd3f19..fea1cbf6 100644 --- a/ojdbc-provider-jackson-oson/src/main/java/oracle/jdbc/provider/oson/ser/OsonSerializableSerializer.java +++ b/ojdbc-provider-jackson-oson/src/main/java/oracle/jdbc/provider/oson/ser/OsonSerializableSerializer.java @@ -1,33 +1,87 @@ +/* + ** Copyright (c) 2024 Oracle and/or its affiliates. + ** + ** The Universal Permissive License (UPL), Version 1.0 + ** + ** Subject to the condition set forth below, permission is hereby granted to any + ** person obtaining a copy of this software, associated documentation and/or data + ** (collectively the "Software"), free of charge and under any and all copyright + ** rights in the Software, and any and all patent rights owned or freely + ** licensable by each licensor hereunder covering either (i) the unmodified + ** Software as contributed to or provided by such licensor, or (ii) the Larger + ** Works (as defined below), to deal in both + ** + ** (a) the Software, and + ** (b) any piece of software and/or hardware listed in the lrgrwrks.txt file if + ** one is included with the Software (each a "Larger Work" to which the Software + ** is contributed by such licensors), + ** + ** without restriction, including without limitation the rights to copy, create + ** derivative works of, display, perform, and distribute the Software and make, + ** use, sell, offer for sale, import, export, have made, and have sold the + ** Software and the Larger Work(s), and to sublicense the foregoing rights on + ** either these or other terms. + ** + ** This license is subject to the following condition: + ** The above copyright notice and either this complete permission notice or at + ** a minimum a reference to the UPL must be included in all copies or + ** substantial portions of the Software. + ** + ** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + ** IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + ** FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + ** AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + ** LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + ** OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + ** SOFTWARE. + */ + package oracle.jdbc.provider.oson.ser; import com.fasterxml.jackson.core.JsonGenerator; import com.fasterxml.jackson.databind.JsonSerializer; import com.fasterxml.jackson.databind.SerializerProvider; -import oracle.jdbc.provider.oson.OsonGenerator; -import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.ObjectOutputStream; +/** + * A custom serializer for serializing objects that implement `Serializable` to a byte array for JSON representation. + * This serializer converts an object into its serialized byte form and writes it as a binary value in the JSON output. + */ public class OsonSerializableSerializer extends JsonSerializer { - public final static OsonSerializableSerializer INSTANCE = new OsonSerializableSerializer(); - - public OsonSerializableSerializer() {} - + /** + * A singleton instance of the `OsonSerializableSerializer` for global use. + */ + public final static OsonSerializableSerializer INSTANCE = new OsonSerializableSerializer(); - @Override - public void serialize(Object value, JsonGenerator gen, SerializerProvider serializers) throws IOException { - if (value == null) { - gen.writeNull(); - } - ByteArrayOutputStream byteStream = new ByteArrayOutputStream(); - try (ObjectOutputStream objectStream = new ObjectOutputStream(byteStream)) { - objectStream.writeObject(value); // Serialize object to byte array - } + /** + * Default constructor. + */ + public OsonSerializableSerializer() {} - byte[] bytes = byteStream.toByteArray(); - gen.writeBinary(bytes); + /** + * Serializes a `Serializable` object into its byte array representation and writes it as a binary value in the JSON. + * If the object is `null`, a `null` value is written. + * + * @param value the object to serialize + * @param gen the JSON generator used for serialization + * @param serializers the serializer provider + * @throws IOException if an I/O error occurs during serialization + */ + @Override + public void serialize(Object value, JsonGenerator gen, SerializerProvider serializers) throws IOException { + if (value == null) { + gen.writeNull(); } + ByteArrayOutputStream byteStream = new ByteArrayOutputStream(); + try (ObjectOutputStream objectStream = new ObjectOutputStream(byteStream)) { + objectStream.writeObject(value); // Serialize object to byte array + } + + byte[] bytes = byteStream.toByteArray(); + gen.writeBinary(bytes); + } }