Set of support modules for Java 8 datatypes (Optionals, date/time) and features (parameter names)

Overview

Overview

This is a multi-module umbrella project for Jackson modules needed to support Java 8 features, especially with Jackson 2.x that only requires Java 7 for running (and until 2.7 only Java 6).

Jackson 2.x

When used with Jackson 2.x, Java 8 support is provided via 3 separate modules:

  • Parameter names: support for detecting constructor and factory method ("creator") parameters without having to use @JsonProperty annotation
    • provides com.fasterxml.jackson.module.paramnames.ParameterNamesModule
  • Java 8 Date/time: support for Java 8 date/time types (specified in JSR-310 specification)
    • provides com.fasterxml.jackson.datatype.jsr310.JavaTimeModule
    • ALSO provides legacy variant com.fasterxml.jackson.datatype.jsr310.JSR310TimeModule
    • difference between 2 modules is that of configuration defaults: use of JavaTimeModule strongly recommended for new code
  • Java 8 Datatypes: support for other new Java 8 datatypes outside of date/time: most notably Optional, OptionalLong, OptionalDouble
    • provides com.fasterxml.jackson.datatype.jdk8.Jdk8Module

all of which are built from this repository, and accessed and used as separate Jackson modules (with separate Maven artifacts).

Jackson 3.0

Jackson 3.0 changes things as it requires Java 8 to work and can thereby directly supported features.

Because of this parameter-names and datatypes modules are merged into jackson-databind and need not be registered; datetime module (JavaTimeModule) remains separate module due to its size and configurability options.

So you will only need to separately add "Java 8 Date/time" module (see above for description)

License

All modules are licensed under Apache License 2.0.

Status

Build Status Tidelift

Usage

Maven dependencies

To include modules, you use some or all of:

<dependency>
    <groupId>com.fasterxml.jackson.module</groupId>
    <artifactId>jackson-module-parameter-names</artifactId>
</dependency>
<dependency>
    <groupId>com.fasterxml.jackson.datatype</groupId>
    <artifactId>jackson-datatype-jdk8</artifactId>
</dependency>
<dependency>
    <groupId>com.fasterxml.jackson.datatype</groupId>
    <artifactId>jackson-datatype-jsr310</artifactId>
</dependency>

and either include versions directly, OR, preferably, import Jackson BOM that will specify consistent version set.

Note that the parent project -- jackson-modules-java8 -- is ONLY used as parent pom by individual "child" modules, and DOES NOT have dependencies on them. This means that you should not depend on it as that will not include child modules.

Registering modules

The most common mechanism (and one recommended by Jackson team) is to explicitly register modules you want. This is done by code like:

// Up to Jackson 2.9: (but not with 3.0)
ObjectMapper mapper = new ObjectMapper()
   .registerModule(new ParameterNamesModule())
   .registerModule(new Jdk8Module())
   .registerModule(new JavaTimeModule()); // new module, NOT JSR310Module

// with 3.0 (or with 2.10 as alternative)
ObjectMapper mapper = JsonMapper.builder() // or different mapper for other format
   .addModule(new ParameterNamesModule())
   .addModule(new Jdk8Module())
   .addModule(new JavaTimeModule())
   // and possibly other configuration, modules, then:
   .build();

Alternatively, you can also auto-discover these modules with:

ObjectMapper mapper = new ObjectMapper();
mapper.findAndRegisterModules();

Regardless of registration mechanism, after registration all functionality is available for all normal Jackson operations.

Notes on Registration

But do note that you should only either explicit OR automatic registration: DO NOT combine explicit and auto-registration. If you use both, only one of registrations will have effect. And selection of which one varies by module and settings:

  • If MapperFeature.IGNORE_DUPLICATE_MODULE_REGISTRATIONS is defined, the FIRST registration succeeds, rest ignored
    • Duplicates are detected using id provided by Module.getTypeId(); duplicate-detection requires that Module provides same for all instances (true for Modules provided by this repo)
  • Otherwise all registrations are processed by the LAST one has effect as it has precedence over earlier registrations.

Also note that before Jackson 2.10, auto-registration would only register older JSR310Module, and not newer JavaTimeModule -- this is due to backwards compatibility. This was changed in Jackson 2.10.

If you want "the other" version of the module but also use auto-registration, make sure to register "other" module explicitly AFTER calling mapper.findAndRegisterModules(). Call after works because getTypeId() provided by modules differs so they are not considered duplicates.

Development

Maintainers

Following developers have committer access to this project.

  • Authors
    • Nick Williams (beamerblvd@github) contributed Java 8 date/time module; still helps issues from time to time
    • Tatu Saloranta (@cowtowncoder) wrote the other 2 modules and maintains them for 2.x (in 3.0, integrated into core jackson-databind)
  • Maintainers:
    • Michael O'Keeffe (kupci@github) is the current maintainer of Java 8 date/time module

More

See Wiki for more information (javadocs).

Comments
  • Unannotated single-argument constructor / factory method not considered a creator

    Unannotated single-argument constructor / factory method not considered a creator

    Take the following domain class on Java 8 compiled with -parameters:

    class SingleValueWrapper {
    
    	private final Integer integer;
    
    	public SingleOtherValue(Integer integer) {
    		this.integer = integer;
    	}
    }
    

    This test:

    @Test
    public void testname() throws Exception {
    
    	ObjectMapper mapper = new ObjectMapper();
    	mapper.registerModule(new ParameterNamesModule());
    
    	SingleValueWrapper value = mapper.readValue("{ \"integer\" : 2 }", SingleValueWrapper.class);
    
    	assertThat(value, is(notNullValue()));
    }
    

    fails with:

    com.fasterxml.jackson.databind.exc.MismatchedInputException: Cannot construct instance of `com.example.LombokImmutablesTests$SingleValueWrapper` (although at least one Creator exists): cannot deserialize from Object value (no delegate- or property-based Creator)
     at [Source: (String)"{ "integer" : 2 }"; line: 1, column: 3]
    	at com.fasterxml.jackson.databind.exc.MismatchedInputException.from(MismatchedInputException.java:63)
    	at com.fasterxml.jackson.databind.DeserializationContext.reportInputMismatch(DeserializationContext.java:1342)
    	at com.fasterxml.jackson.databind.DeserializationContext.handleMissingInstantiator(DeserializationContext.java:1031)
    	at com.fasterxml.jackson.databind.deser.BeanDeserializerBase.deserializeFromObjectUsingNonDefault(BeanDeserializerBase.java:1290)
    	at com.fasterxml.jackson.databind.deser.BeanDeserializer.deserializeFromObject(BeanDeserializer.java:326)
    	at com.fasterxml.jackson.databind.deser.BeanDeserializer.deserialize(BeanDeserializer.java:159)
    	at com.fasterxml.jackson.databind.ObjectMapper._readMapAndClose(ObjectMapper.java:4001)
    	at com.fasterxml.jackson.databind.ObjectMapper.readValue(ObjectMapper.java:2992)
    	at com.example.LombokImmutablesTests.testname4(LombokImmutablesTests.java:73)
    	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    	at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    	at java.lang.reflect.Method.invoke(Method.java:498)
    	at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:50)
    	at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
    	at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:47)
    	at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17)
    	at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:325)
    	at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:78)
    	at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:57)
    	at org.junit.runners.ParentRunner$3.run(ParentRunner.java:290)
    	at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:71)
    	at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:288)
    	at org.junit.runners.ParentRunner.access$000(ParentRunner.java:58)
    	at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:268)
    	at org.junit.runners.ParentRunner.run(ParentRunner.java:363)
    	at org.eclipse.jdt.internal.junit4.runner.JUnit4TestReference.run(JUnit4TestReference.java:86)
    	at org.eclipse.jdt.internal.junit.runner.TestExecution.run(TestExecution.java:38)
    	at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:538)
    	at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:760)
    	at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.run(RemoteTestRunner.java:460)
    	at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.main(RemoteTestRunner.java:206)
    

    A couple of more observations:

    • Adding an explicit annotation (e.g. @ConstructorProperties) makes the test go green. Unfortunately I don't control the class so that I cannot add any annotations. Also, I thought adding the parameter names module should be sufficient as a name can now be derived from the class and additional annotations shouldn't be needed. I.e. the annotation would just redeclare what's already defined.
    • Adding an additional property makes the test pass. This is probably what strikes me most about the problem as it's complete unintuitive why a constructor wouldn't work for one parameter, but would for more than one.
    • The same applies if you rather expose a factory method than a constructor.
    • Even configuring a Mode explicitly doesn't change anything about the failing test.
    • The execution at some point invokes findNameForDeserialization(…) which ´ParameterNamesAnnotationIntrospector` does not override. Is that by design? Because if I go ahead and implement that method like this:
    @Override
    public PropertyName findNameForDeserialization(Annotated a) {
    
    	if (a instanceof AnnotatedParameter) {
    		return PropertyName.construct(findParameterName((AnnotatedParameter) a));
    	}
    
    	return super.findNameForDeserialization(a);
    }
    

    things start to work again. I can see that this method is supposed to explicitly obtain a name from an annotation but it feels like that path is favored for a one-argument constructor.

    opened by odrotbohm 26
  • `DurationDeserializer` should use `@JsonFormat.pattern` (and config override) to support configurable `ChronoUnit`

    `DurationDeserializer` should use `@JsonFormat.pattern` (and config override) to support configurable `ChronoUnit`

    When deserializing DurationDeserializer treats all durations of number values as seconds and delegates string values to Duration.parse.

    Versions

    com.fasterxml.jackson.core:jackson-core:jar:2.11.2
    com.fasterxml.jackson.core:jackson-databind:jar:2.11.2
    com.fasterxml.jackson.datatype:jackson-datatype-jdk8:jar:2.11.2
    com.fasterxml.jackson.datatype:jackson-datatype-jsr310:jar:2.11.2
    com.fasterxml.jackson.core:jackson-annotations:jar:2.11.2
    

    Example

    class A {
        @JsonFormat(pattern = "h")
        Duration dur;
    
        public static void main(String[] args) throws Exception {
            JsonMapper m = JsonMapper.builder()
                    .addModule(new JavaTimeModule())
                    .build();
            A a0 = m.readValue("{\"dur\":2}".getBytes(), A.class);
            System.out.println(a0.dur.equals(Duration.ofHours(2))); // should print "true" but prints "false
    
            A a1 = m.readValue("{\"dur\":\"2\"}".getBytes(), A.class);
            System.out.println(a1.dur.equals(Duration.ofHours(2))); // should print "true" but throws Exception
        }
    }
    

    The Exception thrown by the second readValue call:

    com.fasterxml.jackson.databind.exc.InvalidFormatException: Cannot deserialize value of type `java.time.Duration` from String "2": Failed to deserialize java.time.Duration: (java.time.format.DateTimeParseException) Text cannot be parsed to a Duration
     at [Source: (byte[])"{"dur":"2"}"; line: 1, column: 8] (through reference chain: *****)
    
    	at com.fasterxml.jackson.databind.exc.InvalidFormatException.from(InvalidFormatException.java:67)
    	at com.fasterxml.jackson.databind.DeserializationContext.weirdStringException(DeserializationContext.java:1702)
    	at com.fasterxml.jackson.databind.DeserializationContext.handleWeirdStringValue(DeserializationContext.java:947)
    	at com.fasterxml.jackson.datatype.jsr310.deser.JSR310DeserializerBase._handleDateTimeException(JSR310DeserializerBase.java:129)
    	at com.fasterxml.jackson.datatype.jsr310.deser.DurationDeserializer.deserialize(DurationDeserializer.java:92)
    	at com.fasterxml.jackson.datatype.jsr310.deser.DurationDeserializer.deserialize(DurationDeserializer.java:43)
    	at com.fasterxml.jackson.databind.deser.impl.FieldProperty.deserializeAndSet(FieldProperty.java:138)
    	at com.fasterxml.jackson.databind.deser.BeanDeserializer.vanillaDeserialize(BeanDeserializer.java:293)
    	at com.fasterxml.jackson.databind.deser.BeanDeserializer.deserialize(BeanDeserializer.java:156)
    	at com.fasterxml.jackson.databind.ObjectMapper._readMapAndClose(ObjectMapper.java:4524)
    	at com.fasterxml.jackson.databind.ObjectMapper.readValue(ObjectMapper.java:3527)
            ...
    Caused by: java.time.format.DateTimeParseException: Text cannot be parsed to a Duration
    	at java.base/java.time.Duration.parse(Duration.java:417)
    	at com.fasterxml.jackson.datatype.jsr310.deser.DurationDeserializer.deserialize(DurationDeserializer.java:90)
    	... 70 more
    

    Even though JsonFormat doesn't explicitly mention any jsr310 classes in its documentation they should still work with it. Especially since DurationDeserializer already respects JsonFormat#lenient.

    json-format 
    opened by chisui 25
  • Deserializing ZonedDateTime with basic TZ offset notation (0000)

    Deserializing ZonedDateTime with basic TZ offset notation (0000)

    I stumbled upon deserialization issues when the given string equals to ISO8601 "basic" format.

    2019-08-22T12:36:46.361+0000

    Exception message:

    com.fasterxml.jackson.databind.exc.InvalidFormatException: Cannot deserialize value of type `java.time.ZonedDateTime` from String "2019-08-22T12:36:46.361+0000": Failed to deserialize java.time.ZonedDateTime: (java.time.format.DateTimeParseException) Text '2019-08-22T12:36:46.361+0000' could not be parsed at index 23
     at [Source: (String)""2019-08-22T12:36:46.361+0000""; line: 1, column: 1]
    

    This format misses out a colon in offset in comparison to extended format (as described at: https://bugs.openjdk.java.net/browse/JDK-8176547).

    2019-08-22T12:36:46.361+00:00

    The issue states that java.time.format.DateTimeFormatter.ISO_ZONED_DATE_TIME does not support this basic format.

    However, as this format is provided by Spring exception handler I was wondering if there exists any workaround to successfully parse this format. Maybe I am simply missing the obvious here?

    As current workaround I had to register a custom InstantDeserializer and set com.fasterxml.jackson.datatype.jsr310.deser.InstantDeserializer.replaceZeroOffsetAsZ to true. Any hint is very much appreciated.

    Reproduce

    		ZonedDateTime zdt1 = ZonedDateTime.now();
    		ObjectMapper mapper = new ObjectMapper()                                         
                                            .registerModule(new ParameterNamesModule())
    					.registerModule(new Jdk8Module())
                                            .registerModule(new JavaTimeModule());
    
    		//happy path: serialize and deserialize
    		String zdt1S = mapper.writeValueAsString(zdt1);
    		ZonedDateTime zdt1R = mapper.readValue(zdt1S, ZonedDateTime.class);
    		assertThat(zdt1R, notNullValue());
    		gLogger.info("zdt1S: " + zdt1R);
    
    		//ZonedDateTime with basic formatted offset leads to exception
    		String basicTS = "\"2019-08-22T12:36:46.361+0000\"";
    		ZonedDateTime zdt2 = mapper.readValue(basicTS, ZonedDateTime.class);
    		gLogger.info("zdt2S: " + zdt2);
    
    opened by noway1979 18
  • `ParameterNamesModule` does not deserialize with a single parameter constructor when using `SnakeCase` `PropertyNamingStrategy`

    `ParameterNamesModule` does not deserialize with a single parameter constructor when using `SnakeCase` `PropertyNamingStrategy`

    Since release 2.9.2, the following test that passes with previous versions is broken.

    import com.fasterxml.jackson.annotation.JsonCreator;
    import com.fasterxml.jackson.databind.ObjectMapper;
    import com.fasterxml.jackson.databind.PropertyNamingStrategy;
    
    import org.junit.Test;
    
    import static org.assertj.core.api.BDDAssertions.*;
    
    public class JsonCreatorSnakeCaseNamingTest
    {
    	@Test
    	public void shouldDeserializeClassWithJsonCreatorWithSnakeCaseNaming() throws Exception {
    
    		// given
    		ObjectMapper objectMapper = new ObjectMapper();
    		objectMapper.registerModule(new ParameterNamesModule());
    		objectMapper.setPropertyNamingStrategy(PropertyNamingStrategy.SNAKE_CASE);
    
    		// when
    		String json = "{\"first_property\":\"1st\"}";
    		ClassWithOneProperty actual = objectMapper.readValue(json, ClassWithOneProperty.class);
    
    		then(actual).isEqualToComparingFieldByField(new ClassWithOneProperty("1st"));
    	}
    
    	static class ClassWithOneProperty {
    		public final String firstProperty;
    
    		@JsonCreator
    		public ClassWithOneProperty(String firstProperty) {
    			this.firstProperty = firstProperty;
    		}
    	}
    }
    

    The test passes with if I create the ParameterNamesModule with JsonCreator.Mode.PROPERTIES as the argument.

    Is this intended? If so, this should probably be documented as a breaking change. Otherwise existing code bases break without warning.

    It appears that the code responsible for this is BasicDeserializerFactory._addExplicitAnyCreator which was newly added in 2.9.2, but I am not certain.

    Should I wait for a fix, or switch to using new ParameterNamesModule(JsonCreator.Mode.PROPERTIES)? Can that have any side effects that I should watch out for?

    Thanks.

    • Sonny
    opened by sonnygill 16
  • code to handle FasterXML/jackson-databind/issues/2141

    code to handle FasterXML/jackson-databind/issues/2141

    Guards against numbers causing CPU or OOM issues when deserializing large numbers into Instant or Duration (see https://github.com/FasterXML/jackson-databind/issues/2141 ) by either:

    • Scientific notation too large (eg 10000e100000)
    • Raw string repesenting a number of length too long
    opened by abracadv8 15
  • Serialization of ZonedDateTime with ZoneId bug since 2.12

    Serialization of ZonedDateTime with ZoneId bug since 2.12

    Hello,

    I have a simple test case, where I expect my ObjectMapper to keep the ZoneId I've put in my code, for example UTC, Europe/Paris, ... during Serialization.

    If I read the documentation

    For TimeZone handling, ADJUST_DATES_TO_CONTEXT_TIME_ZONE (default: true) specifies whether the context provided by java.time.TimeZone 'SerializedProvider#getTimeZone()' should be used to adjust Date/Time values on deserialization, even if the value itself contains timezone information. If the value is OffsetDateTime.MIN or OffsetDateTime.MAX, the Date/Time value will not be adjusted. If disabled, it will only be used if the value itself does not contain any TimeZone information.

    Here is my ObjectMapper configuration :

            final JavaTimeModule module = new JavaTimeModule();
            mapper = new ObjectMapper() //
                .setSerializationInclusion(Include.NON_NULL) //
                .disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS) //
                .disable(DeserializationFeature.ADJUST_DATES_TO_CONTEXT_TIME_ZONE) //
                .setTimeZone(TimeZone.getTimeZone("Europe/Paris")) //
                .registerModule(module);
    

    If I test :

            final BeanForJson bean = new BeanForJson();
            bean.setZonedDateTime(ZonedDateTime.of(2016, 10, 15, 17, 51, 50, 0, ZoneId.of("UTC")));
    
            final String json = mapper.writeValueAsString(bean);
            assertThat(json).isEqualTo("{\"zonedDateTime\":\"2016-10-15T17:51:50Z\"}");
    

    I get :

    org.opentest4j.AssertionFailedError: 
    expected: "{"zonedDateTime":"2016-10-15T17:51:50Z"}"
    but was : "{"zonedDateTime":"2016-10-15T19:51:50+02:00"}"
    

    It is working in 2.11.4 (same use-case).

    Thanks

    date-time-config 
    opened by vmeunier 13
  • Support use of

    Support use of "pattern" (`ChronoUnit`) for `DurationSerializer` too

    (see #184 for background)

    So, we should support use of @JsonFormat(pattern = "HOURS") (and "config override" equivalent) for writing too. As discussed on #184, there is gnarly challenge with backwards-compatibility -- change should only affect explicit pattern-using case.

    Another challenge is the possibility of fractional values: given Duration may not be exactly divisible by desired unit: for example, we might have "90 seconds" as value, but configured to use "minutes", resulting in 1.5 (ideally). If it is possible to calculate this accurately (enough), I'd like to do that, but I am not sure it is.

    If not, I suggest that only one specific case of fractions would be supported: that of "SECONDS" as explicit unit. It would be output as fractions as needed, based on underlying seconds/nanoseconds values.

    hacktoberfest 
    opened by cowtowncoder 12
  • Unexpected ValueInstantiationException if register ParameterNamesModule

    Unexpected ValueInstantiationException if register ParameterNamesModule

    I'm deserialize COSE key, every thing works fine with version 2.10.1, but failed with ParameterNamesModule registered since I want object immutable, I'm not sure this is same as https://github.com/FasterXML/jackson-modules-java8/issues/123. here is simple test project pname.zip

    test-needed 
    opened by quaff 12
  • Why is there no concrete `OffsetDateTimeDeserializer` class to use via annotations

    Why is there no concrete `OffsetDateTimeDeserializer` class to use via annotations

    I am wondering why there is com.fasterxml.jackson.datatype.jsr310.ser.OffsetDateTimeSerializer but no com.fasterxml.jackson.datatype.jsr310.deser.OffsetDateTimeDeserializer

    Without that it seems it's not possible to configure serialization behaviour only via @JsonSerialize and @JsonDeserialize annotations.

    I have classes with fields of type OffsetDateTime. Annotating these with the respective annotation works like a charm for serialization, but for deserialization I'm stuck with explicitly adding the JavaTimeModule to the object mapper, which I'd prefer to do without.

    As the annotations need objects of type Class<? extends JsonDeserializer> to be passed as values to the using parameters, there currently seems to be no way of using these for deserialization.

    Is there any specific reason to only have a smaller subset of explicit *Deserializer classes? Or is there some other way to use these annotations which I'm just missing here?

    good first issue documentation hacktoberfest 
    opened by uqmat 12
  • (datatypes) Add Serialization Support for Streams

    (datatypes) Add Serialization Support for Streams

    (moved from earlier issue filed by @jmax01)

    Here is a first pass at serializing Streams. It works for 2.6.x and above. A 2.8.1 version is also shown.

    
    import java.io.IOException;
    import java.lang.reflect.Type;
    import java.util.Iterator;
    import java.util.stream.Stream;
    
    import com.fasterxml.jackson.core.JsonGenerator;
    import com.fasterxml.jackson.databind.AnnotationIntrospector;
    import com.fasterxml.jackson.databind.BeanDescription;
    import com.fasterxml.jackson.databind.BeanProperty;
    import com.fasterxml.jackson.databind.JavaType;
    import com.fasterxml.jackson.databind.JsonSerializer;
    import com.fasterxml.jackson.databind.MapperFeature;
    import com.fasterxml.jackson.databind.SerializationConfig;
    import com.fasterxml.jackson.databind.SerializerProvider;
    import com.fasterxml.jackson.databind.annotation.JsonSerialize;
    import com.fasterxml.jackson.databind.jsontype.TypeSerializer;
    import com.fasterxml.jackson.databind.module.SimpleModule;
    import com.fasterxml.jackson.databind.ser.BasicSerializerFactory;
    import com.fasterxml.jackson.databind.ser.BeanSerializerFactory;
    import com.fasterxml.jackson.databind.ser.std.AsArraySerializerBase;
    import com.fasterxml.jackson.databind.type.CollectionLikeType;
    import com.fasterxml.jackson.databind.type.TypeBindings;
    import com.fasterxml.jackson.databind.type.TypeFactory;
    import com.fasterxml.jackson.databind.type.TypeModifier;
    
    /**
     * The Class StreamModule.
     *
     * @author jmaxwell
     */
    public class StreamModule extends SimpleModule {
    
        /** The Constant serialVersionUID. */
        private static final long serialVersionUID = -1324033833221219001L;
    
        @Override
        public void setupModule(final SetupContext context) {
            context.addTypeModifier(new StreamTypeModifier());
            context.addSerializers(new StreamSerializers());
        }
    
        /**
         * The Class StreamTypeModifier.
         */
        public static final class StreamTypeModifier extends TypeModifier {
    
            /**
             * Tested for both 2.6.x and 2.8.1
             */
            @Override
            public JavaType modifyType(final JavaType type, final Type jdkType, final TypeBindings context,
                    final TypeFactory typeFactory) {
    
                if (type.isReferenceType() || type.isContainerType()) {
                    return type;
                }
    
                final Class<?> raw = type.getRawClass();
    
                if (Stream.class.isAssignableFrom(raw)) {
    
                    final JavaType[] params = typeFactory.findTypeParameters(type, Stream.class);
    
                    if (params == null || params.length == 0) {
    
                        return typeFactory.constructReferenceType(raw, TypeFactory.unknownType());
                    }
    
                    return typeFactory.constructCollectionLikeType(raw, params[0]);
                }
                return type;
            }
    
            //
            // the 2.8.1 and above way
            // @Override
            // public JavaType modifyType(JavaType type, Type jdkType, TypeBindings context, TypeFactory typeFactory) {
            //
            // if (type.isReferenceType() || type.isContainerType()) {
            // return type;
            // }
            //
            // Class<?> raw = type.getRawClass();
            //
            // if (Stream.class.isAssignableFrom(raw)) {
            //
            // JavaType[] params = typeFactory.findTypeParameters(type, Stream.class);
            //
            // if (params == null || params.length == 0) {
            //
            // return ReferenceType.upgradeFrom(type, type.containedTypeOrUnknown(0));
            // }
            //
            // return typeFactory.constructCollectionLikeType(raw, params[0]);
            //
            // }
            // return type;
            // }
            //
    
        }
    
        /**
         * The Class StreamSerializers.
         */
        public static final class StreamSerializers extends com.fasterxml.jackson.databind.ser.Serializers.Base {
    
            @Override
            public JsonSerializer<?> findCollectionLikeSerializer(final SerializationConfig config,
                    final CollectionLikeType type, final BeanDescription beanDesc,
                    final TypeSerializer elementTypeSerializer, final JsonSerializer<Object> elementValueSerializer) {
    
                final Class<?> raw = type.getRawClass();
    
                if (Stream.class.isAssignableFrom(raw)) {
    
                    final TypeFactory typeFactory = config.getTypeFactory();
    
                    final JavaType[] params = typeFactory.findTypeParameters(type, Stream.class);
    
                    final JavaType vt = (params == null || params.length != 1) ? TypeFactory.unknownType() : params[0];
    
                    return new StreamSerializer(type.getContentType(), usesStaticTyping(config, beanDesc, null),
                            BeanSerializerFactory.instance.createTypeSerializer(config, vt));
                }
    
                return null;
            }
    
            /**
             * Uses static typing. Copied from {@link BasicSerializerFactory}
             *
             * @param config the config
             * @param beanDesc the bean desc
             * @param typeSer the type ser
             * @return true, if successful
             */
            private static final boolean usesStaticTyping(final SerializationConfig config, final BeanDescription beanDesc,
                    final TypeSerializer typeSer) {
                /*
                 * 16-Aug-2010, tatu: If there is a (value) type serializer, we can not force
                 * static typing; that would make it impossible to handle expected subtypes
                 */
                if (typeSer != null) {
                    return false;
                }
                final AnnotationIntrospector intr = config.getAnnotationIntrospector();
                final JsonSerialize.Typing t = intr.findSerializationTyping(beanDesc.getClassInfo());
                if (t != null && t != JsonSerialize.Typing.DEFAULT_TYPING) {
                    return (t == JsonSerialize.Typing.STATIC);
                }
                return config.isEnabled(MapperFeature.USE_STATIC_TYPING);
            }
    
            /**
             * The Class StreamSerializer.
             */
            public static final class StreamSerializer extends AsArraySerializerBase<Stream<?>> {
    
                /** The Constant serialVersionUID. */
                private static final long serialVersionUID = -455534622397905995L;
    
                /**
                 * Instantiates a new stream serializer.
                 *
                 * @param elemType the elem type
                 * @param staticTyping the static typing
                 * @param vts the vts
                 */
                public StreamSerializer(final JavaType elemType, final boolean staticTyping, final TypeSerializer vts) {
                    super(Stream.class, elemType, staticTyping, vts, null);
                }
    
                /**
                 * Instantiates a new stream serializer.
                 *
                 * @param src the src
                 * @param property the property
                 * @param vts the vts
                 * @param valueSerializer the value serializer
                 */
                public StreamSerializer(final StreamSerializer src, final BeanProperty property, final TypeSerializer vts,
                        final JsonSerializer<?> valueSerializer) {
                    super(src, property, vts, valueSerializer, false);
                }
    
                @Override
                public void serialize(final Stream<?> value, final JsonGenerator gen, final SerializerProvider provider)
                        throws IOException {
                    this.serializeContents(value, gen, provider);
                }
    
                /**
                 * withResolved.
                 *
                 * @param property the property
                 * @param vts the vts
                 * @param elementSerializer the element serializer
                 * @param unwrapSingle ignored always false since streams are one time use I don't believe we can get a
                 *            single element
                 * @return the as array serializer base
                 */
                @Override
                public StreamSerializer withResolved(final BeanProperty property, final TypeSerializer vts,
                        final JsonSerializer<?> elementSerializer, final Boolean unwrapSingle) {
                    return new StreamSerializer(this, property, vts, elementSerializer);
                }
    
                @Override
                protected void serializeContents(final Stream<?> value, final JsonGenerator gen,
                        final SerializerProvider provider) throws IOException {
    
                    provider.findValueSerializer(Iterator.class, null)
                        .serialize(value.iterator(), gen, provider);
    
                }
    
                @Override
                public boolean hasSingleElement(final Stream<?> value) {
                    // no really good way to determine (without consuming stream), so:
                    return false;
                }
    
                @Override
                protected StreamSerializer _withValueTypeSerializer(final TypeSerializer vts) {
    
                    return new StreamSerializer(this, this._property, vts, this._elementSerializer);
                }
            }
        }
    }
    

    Tests:

    
    import static org.junit.Assert.*;
    
    import java.io.IOException;
    import java.util.ArrayList;
    import java.util.Collection;
    import java.util.List;
    import java.util.stream.Stream;
    
    import org.junit.Test;
    
    import com.fasterxml.jackson.core.type.TypeReference;
    import com.fasterxml.jackson.databind.DeserializationFeature;
    import com.fasterxml.jackson.databind.ObjectMapper;
    import com.fasterxml.jackson.databind.SerializationFeature;
    import com.fasterxml.jackson.datatype.guava.GuavaModule;
    import com.fasterxml.jackson.datatype.jdk8.Jdk8Module;
    import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
    import com.fasterxml.jackson.module.afterburner.AfterburnerModule;
    import com.fasterxml.jackson.module.mrbean.MrBeanModule;
    import com.fasterxml.jackson.module.paramnames.ParameterNamesModule;
    import com.theice.cds.common.serialization.json.jackson2.StreamModule;
    
    @SuppressWarnings("javadoc")
    public class StreamModuleTest {
    
        public static final ObjectMapper OBJECT_MAPPER = new ObjectMapper().registerModule(new GuavaModule())
            .registerModule(new Jdk8Module())
            .registerModule(new JavaTimeModule())
            .registerModule(new ParameterNamesModule())
            .registerModule(new AfterburnerModule())
            .registerModule(new StreamModule())
            .registerModule(new MrBeanModule());
    
        static <T> void assertRoundTrip(final Collection<T> original, final ObjectMapper objectMapper) throws IOException {
    
            final Stream<T> asStream = original.stream();
    
            final String asJsonString = objectMapper.writeValueAsString(asStream);
    
            System.out.println("original: " + original + " -> " + asJsonString);
    
            final Collection<T> fromJsonString = OBJECT_MAPPER.readValue(asJsonString,
                    new TypeReference<Collection<T>>() {});
    
            assertEquals(original, fromJsonString);
        }
    
        @SuppressWarnings("deprecation")
        @Test
        public void testEmptyStream() throws IOException {
    
            assertRoundTrip(new ArrayList<>(), OBJECT_MAPPER.copy()
                .enable(DeserializationFeature.ACCEPT_EMPTY_ARRAY_AS_NULL_OBJECT));
    
            // shouldn't this fail?
            assertRoundTrip(new ArrayList<>(), OBJECT_MAPPER.copy()
                .disable(SerializationFeature.WRITE_EMPTY_JSON_ARRAYS)
                .disable(DeserializationFeature.ACCEPT_EMPTY_ARRAY_AS_NULL_OBJECT));
        }
    
        @Test
        public void testSingleElementStream() throws IOException {
    
            final List<String> collection = new ArrayList<>();
            collection.add("element1");
    
            assertRoundTrip(collection, OBJECT_MAPPER.copy()
                .enable(SerializationFeature.WRITE_SINGLE_ELEM_ARRAYS_UNWRAPPED)
                .enable(DeserializationFeature.ACCEPT_SINGLE_VALUE_AS_ARRAY));
    
            assertRoundTrip(collection, OBJECT_MAPPER.copy()
                .disable(SerializationFeature.WRITE_SINGLE_ELEM_ARRAYS_UNWRAPPED)
                .disable(DeserializationFeature.ACCEPT_SINGLE_VALUE_AS_ARRAY));
    
            // should fail but can't for stream
            assertRoundTrip(collection, OBJECT_MAPPER.copy()
                .enable(SerializationFeature.WRITE_SINGLE_ELEM_ARRAYS_UNWRAPPED)
                .disable(DeserializationFeature.ACCEPT_SINGLE_VALUE_AS_ARRAY));
        }
    
        @Test
        public void testMultipleElementStream() throws IOException {
    
            final List<String> collection = new ArrayList<>();
            collection.add("element1");
            collection.add("element2");
    
            assertRoundTrip(collection, OBJECT_MAPPER);
    
        }
    }
    
    opened by cowtowncoder 12
  • Add/update unit tests when allow strict custom format

    Add/update unit tests when allow strict custom format

    WHY

    See issue #199

    We'd better add some unit tests to make sure the original behaviour works when we allow strict LocalDate parsing.

    HOW

    • Add test cases for format pattern yyyy-MM-dd G in strict mode(y means year of the era in DateTimeFormatter and should use together with G. ).
    • Add test cases for format pattern uuuu-MM-dd in strict mode.
    date-time-config 
    opened by Fatezhang 11
  • Optional object Serialization Whole Object required instead of value.

    Optional object Serialization Whole Object required instead of value.

    • Json Serialization for a Optional value just has value object.
    • I want the whole Optional object to be serialized. Is there a way I can enable that using this module.

    What I want

    {
        "present": true,
         "value": {
                "id": 8
          }
    }
    

    What I get right now

    {
       "id":  8
    }
    
    opened by prakharporwal 0
  • Incorrect module auto-registred when using JPMS

    Incorrect module auto-registred when using JPMS

    As I understand module com.fasterxml.jackson.datatype.jsr310.JSR310Module is left for compatibility and com.fasterxml.jackson.datatype.jsr310.JavaTimeModule should be default one. However when using ObjectMapper.findAndRegisterModules() with JPMS in Java9+ JSR310Module is loaded as declared in module-info.java.

    It should be changed as in #126 to maintain same behavior between modularized and non-modularized environments.

    opened by karbi 0
  • LocalDate Deserializer not being invoked.

    LocalDate Deserializer not being invoked.

    I have a class

    package com.walmart.polaris.schedule.rule.model.schedule;

    import com.fasterxml.jackson.annotation.JsonAlias; import com.fasterxml.jackson.annotation.JsonFormat; import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateDeserializer; import com.walmart.polaris.schedule.rule.constants.WorkerStatus; import com.walmart.polaris.schedule.rule.model.common.WorkerType; import com.walmart.polaris.schedule.rule.model.common.AvailabilityException; import com.walmart.polaris.schedule.rule.model.common.Job; import com.walmart.polaris.schedule.rule.model.common.Location; import com.walmart.polaris.schedule.rule.model.common.PayType; import com.walmart.polaris.schedule.rule.model.common.Shift; import java.time.LocalDate; import java.util.List;

    import com.walmart.polaris.schedule.rule.util.DateDeserializer; import com.walmart.polaris.schedule.rule.util.DateSereliazer; import lombok.*; import org.codehaus.jackson.map.annotate.JsonDeserialize; import org.codehaus.jackson.map.annotate.JsonSerialize;

    @Data @Builder @AllArgsConstructor @NoArgsConstructor @ToString public class ValidationRequest { @JsonAlias("id") private Long workerId; private String requestId; private List locations;

    @JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "dd-MM-yyyy") @JsonDeserialize(using = DateDeserializer.class) @JsonSerialize(using = DateSereliazer.class) private LocalDate birthDate;

    private WorkerStatus status; @JsonAlias("employmentType") private List workerType; private List payType; private List availabilityExceptions; private List jobs; private List schedules; }

    And I wrote a customDeserializer for one property birthDate which is of type LocalDate.

    package com.walmart.polaris.schedule.rule.util;

    import lombok.extern.slf4j.Slf4j; import org.codehaus.jackson.JsonParser; import org.codehaus.jackson.map.DeserializationContext; import org.codehaus.jackson.map.JsonDeserializer;

    import java.io.IOException; import java.time.*; import java.time.format.DateTimeFormatter; import java.time.format.DateTimeParseException;

    @Slf4j public class DateDeserializer extends JsonDeserializer {

    public DateDeserializer() {
        this(null);
    }
    
    public DateDeserializer(Class<?> vc) {
        super();
    }
    @Override
    public LocalDate deserialize(JsonParser p, DeserializationContext ctx)
            throws IOException {
        String str = p.getText();
        try {
            return LocalDate.parse(str, DateTimeFormatter.ofPattern("dd-MM-yyyy"));
        } catch (DateTimeParseException e) {
            return LocalDate.parse(str, DateTimeFormatter.ofPattern("yyyy-MM-dd"));
        }
    }
    

    }

    My use case is that the property birthDate can be of format dd-MM-yyyy or yyyy-MM-dd. Hence I tried to parse it using both patterns. But there seems to be some issue in this, the deserialize method is not being invoked. I also tried removing the @JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "dd-MM-yyyy") line from it, then also same issue is occuring. Can anyone help me? Or what am I doing wrong here?

    opened by sanchayddun 0
  • Cannot deserialize value of type `java.time.LocalDate` from String

    Cannot deserialize value of type `java.time.LocalDate` from String "07-OCT-21": Failed to deserialize java.time.LocalDate:

    I am getting following exception.

    Exception in thread "main" com.fasterxml.jackson.databind.exc.InvalidFormatException: Cannot deserialize value of type `java.time.LocalDate` from String "07-OCT-21": Failed to deserialize java.time.LocalDate: (java.time.format.DateTimeParseException) Text '07-OCT-21' could not be parsed at index 0
     at [Source: (String)"{"name":"party","eventDate":"07-OCT-21"}"; line: 1, column: 29] (through reference chain: jolt.custom.date.transformer.model.Event["eventDate"])
    	at com.fasterxml.jackson.databind.exc.InvalidFormatException.from(InvalidFormatException.java:67)
    	at com.fasterxml.jackson.databind.DeserializationContext.weirdStringException(DeserializationContext.java:1991)
    	at com.fasterxml.jackson.databind.DeserializationContext.handleWeirdStringValue(DeserializationContext.java:1219)
    	at com.fasterxml.jackson.datatype.jsr310.deser.JSR310DeserializerBase._handleDateTimeException(JSR310DeserializerBase.java:176)
    	at com.fasterxml.jackson.datatype.jsr310.deser.LocalDateDeserializer._fromString(LocalDateDeserializer.java:179)
    	at com.fasterxml.jackson.datatype.jsr310.deser.LocalDateDeserializer.deserialize(LocalDateDeserializer.java:92)
    	at com.fasterxml.jackson.datatype.jsr310.deser.LocalDateDeserializer.deserialize(LocalDateDeserializer.java:38)
    	at com.fasterxml.jackson.databind.deser.impl.MethodProperty.deserializeAndSet(MethodProperty.java:129)
    	at com.fasterxml.jackson.databind.deser.BeanDeserializer.vanillaDeserialize(BeanDeserializer.java:313)
    	at com.fasterxml.jackson.databind.deser.BeanDeserializer.deserialize(BeanDeserializer.java:176)
    	at com.fasterxml.jackson.databind.deser.DefaultDeserializationContext.readRootValue(DefaultDeserializationContext.java:322)
    	at com.fasterxml.jackson.databind.ObjectMapper._readMapAndClose(ObjectMapper.java:4674)
    	at com.fasterxml.jackson.databind.ObjectMapper.readValue(ObjectMapper.java:3629)
    	at com.fasterxml.jackson.databind.ObjectMapper.readValue(ObjectMapper.java:3597)
    	at jolt.custom.date.transformer.JacksonDateFormter.main(JacksonDateFormter.java:46)
    Caused by: java.time.format.DateTimeParseException: Text '07-OCT-21' could not be parsed at index 0
    	at java.base/java.time.format.DateTimeFormatter.parseResolved0(DateTimeFormatter.java:2052)
    	at java.base/java.time.format.DateTimeFormatter.parse(DateTimeFormatter.java:1954)
    	at java.base/java.time.LocalDate.parse(LocalDate.java:430)
    	at com.fasterxml.jackson.datatype.jsr310.deser.LocalDateDeserializer._fromString(LocalDateDeserializer.java:177)
    	... 10 more
    

    Scenario:- I am getting a date from downstream API in dd-MMM-yy format e.g 07-OCT-21 my requirement is my API should return date in ISO_DATE format i.e. 'yyyy-MM-dd'. We have swagger specification files using which we are generating a boilerplate code using swaggger-codegen maven plugin target. So it adds @DateTimeFormat(iso = DateTimeFormat.ISO.DATE) annotation for date.

    From mapper my expectation is while mapping json to java class it should transform date from dd-MMM-yy to yyyy-MM-dd format as per ISO_DATE format.

    code to reproduce

    Sample response coming from downstream application with date dd-MMM-yy format src/main/resources/event1.json

    {
       "name":"party",
       "eventDate":"07-OCT-21"
    }
    

    Event.java

    import org.springframework.format.annotation.DateTimeFormat;
    
    import lombok.EqualsAndHashCode;
    import lombok.Getter;
    import lombok.Setter;
    
    @Setter
    @Getter
    @EqualsAndHashCode
    public class Event {
        public String name;
    
        @DateTimeFormat(iso = DateTimeFormat.ISO.DATE)
        public LocalDate eventDate;
    
        @Override
        public String toString() {
            // TODO Auto-generated method stub
            return getName() + " : " + getEventDate().toString();
        }
    }
    

    JacksonDateFormter.java

    import java.text.ParseException;
    import java.text.SimpleDateFormat;
    import java.time.LocalDate;
    import java.time.LocalDateTime;
    import java.time.format.DateTimeFormatter;
    
    import com.bazaarvoice.jolt.JsonUtils;
    import com.fasterxml.jackson.core.JsonProcessingException;
    import com.fasterxml.jackson.databind.JsonMappingException;
    import com.fasterxml.jackson.databind.ObjectMapper;
    import com.fasterxml.jackson.databind.SerializationFeature;
    import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
    import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateTimeDeserializer;
    
    import jolt.custom.date.transformer.model.Event;
    
    public class JacksonDateFormter {
    
        public static void main(String[] args) throws ParseException, JsonMappingException, JsonProcessingException {
    
           
    
            Object object = JsonUtils.classpathToObject("/event1.json");
    
            String eventString = JsonUtils.toJsonString(object);
    
            ObjectMapper mapper = new ObjectMapper();
    
            JavaTimeModule javaTimeModule = new JavaTimeModule();
            mapper.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS);
            mapper.setDateFormat(new SimpleDateFormat("yyyy-MM-dd"));
            javaTimeModule.addDeserializer(LocalDateTime.class, new LocalDateTimeDeserializer(DateTimeFormatter.ISO_DATE));
    
            mapper.registerModule(javaTimeModule);
    
            mapper.readValue(eventString, Event.class);
    
        }
    
    }
    
    

    pom.xml

    <project xmlns="http://maven.apache.org/POM/4.0.0"
      xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
      xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
      <modelVersion>4.0.0</modelVersion>
      <groupId>jolt-json-transformer</groupId>
      <artifactId>jolt-json-transformer</artifactId>
      <version>0.0.1-SNAPSHOT</version>
      <name>jolt-json-transformer</name>
      <properties>
        <maven.compiler.source>17</maven.compiler.source>
        <maven.compiler.target>17</maven.compiler.target>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
      </properties>
      <dependencies>
        <dependency>
          <groupId>com.bazaarvoice.jolt</groupId>
          <artifactId>json-utils</artifactId>
          <version>0.1.7</version>
        </dependency>
        <!-- https://mvnrepository.com/artifact/com.bazaarvoice.jolt/jolt-core -->
        <dependency>
          <groupId>com.bazaarvoice.jolt</groupId>
          <artifactId>jolt-core</artifactId>
          <version>0.1.7</version>
        </dependency>
    
        <!-- https://mvnrepository.com/artifact/org.springframework/spring-context -->
        <dependency>
          <groupId>org.springframework</groupId>
          <artifactId>spring-context</artifactId>
          <version>6.0.2</version>
        </dependency>
        <!-- https://mvnrepository.com/artifact/org.projectlombok/lombok -->
        <dependency>
          <groupId>org.projectlombok</groupId>
          <artifactId>lombok</artifactId>
          <version>1.18.24</version>
          <scope>provided</scope>
        </dependency>
        <!-- https://mvnrepository.com/artifact/com.fasterxml.jackson.datatype/jackson-datatype-jsr310 -->
        <dependency>
          <groupId>com.fasterxml.jackson.datatype</groupId>
          <artifactId>jackson-datatype-jsr310</artifactId>
          <version>2.14.1</version>
        </dependency>
    
      </dependencies>
    </project>
    
    
    has-failing-test 
    opened by Prasaddiwalkar 8
  • @JsonFormat annotation doesn't work for OffsetDateTime when configuring Global OffsetDateTimeSerializer

    @JsonFormat annotation doesn't work for OffsetDateTime when configuring Global OffsetDateTimeSerializer

    After configuring ObjectMapper globally and with annotation, serialization works expectedly on Date and LocalDateTime types.

    As for OffsetDateTime, @JsonFormat annotation doesn't work and mapper always use Global config.

    In order to solve the issue, I need to use custom serializer and @JsonSerialize annotation as below, @JsonSerialize(using = OffsetDateTimeSerializerImpl.class) private OffsetDateTime offsetDateTimeAnnotated;

    Note: JsonMapper has the same problem with ObjectMapper

    Maven:

    <dependency>
      <groupId>com.fasterxml.jackson.datatype</groupId>
      <artifactId>jackson-datatype-jsr310</artifactId>
      <version>2.3.14</version>
    </dependency>
    

    Formatters:

    For Global ('G' between date and time) Date: yyyy-MM-dd'G'HH:mm:ss LocalDateTime: yyyy-MM-dd'G'HH:mm:ss.SSS OffsetDateTime: yyyy-MM-dd'G'HH:mm:ssZ

    For Annotation ('A' between date and time) Date: yyyy-MM-dd'A'HH:mm:ss LocalDateTime: yyyy-MM-dd'A'HH:mm:ss.SSS OffsetDateTime: yyyy-MM-dd'A'HH:mm:ssZ

    Result: { "date" : "2022-12-02G17:29:02", "dateAnnotated" : "2022-12-02A17:29:02", "localDateTime" : "2022-12-02G17:29:02.194", "localDateTimeAnnotated" : "2022-12-02A17:29:02.194", "offsetDateTime" : "2022-12-02G17:29:02+0300", "offsetDateTimeAnnotated" : "2022-12-02G17:29:02+0300" <-- It must be in format: "yyyy-MM-dd'A'HH:mm:ssZ" }

       @Bean
        public ObjectMapper objectMapper() {
            ObjectMapper mapper = new ObjectMapper()
                    .registerModule(new JavaTimeModule())
                    .disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS);
    
            // for Date
            mapper.setDateFormat("yyyy-MM-dd-HH:mm:ss");
            mapper.setTimeZone(TimeZone.getDefault());
    
            // for LocalDateTime
            SimpleModule simpleModule = new SimpleModule();
            simpleModule.addSerializer(new LocalDateTimeSerializer(DateTimeFormatter.ofPattern("yyyy-MM-dd'G'HH:mm:ss.SSS")));
    
            // for OffsetDateTime
    
            // with serializer:
            // -  @JsonFormat doesn't work
    
            // without serializer:
            // -  @JsonFormat works
            // -  but no control over global format, it becomes ISO_OFFSET_DATE_TIME
    
           simpleModule.addSerializer(OffsetDateTime.class, new JsonSerializer<OffsetDateTime>() {
                @Override
                public void serialize(OffsetDateTime offsetDateTime, JsonGenerator jsonGenerator, SerializerProvider serializerProvider) throws IOException, JsonProcessingException {
                    jsonGenerator.writeString(DateTimeFormatter.ofPattern("yyyy-MM-dd'G'HH:mm:ssZ").format(offsetDateTime));
                }
            });
    
            mapper.registerModule(simpleModule);
            return mapper;
        }
    
    public class DateFormats {
    
        private Date date;
    
        @JsonFormat(pattern="yyyy-MM-dd'A'HH:mm:ss")
        private Date dateAnnotated;
    
        private LocalDateTime localDateTime;
    
        @JsonFormat(pattern="yyyy-MM-dd'A'HH:mm:ss.SSS")
        private LocalDateTime localDateTimeAnnotated;
    
        private OffsetDateTime offsetDateTime;
    
        @JsonFormat(pattern="yyyy-MM-dd'A'HH:mm:ssZ")
         private OffsetDateTime offsetDateTimeAnnotated;
    
        public DateFormats() {
    
            this.dateAnnotated = Calendar.getInstance().getTime();
            this.date = Calendar.getInstance().getTime();
    
            this.offsetDateTimeAnnotated = OffsetDateTime.now();
            this.offsetDateTime = OffsetDateTime.now();
    
            this.localDateTimeAnnotated = LocalDateTime.now();
            this.localDateTime = LocalDateTime.now();
        }
    
        // getter and setters
    }
    
    test-needed 2.14 
    opened by yuzmann 3
Owner
FasterXML, LLC
FasterXML, LLC
It generates a circular Image from users display names with animation support

DisplayNameView generates an avatar from users name. Setup allprojects { repositories { ... maven { url 'https://jitpack.io' }

Emre 4 Sep 24, 2021
🍬A set of tools that keep Java sweet.

?? A set of tools that keep Java sweet. ?? https://hutool.cn/ ?? ?? English Documentation ?? 简介 Hutool是一个小而全的Java工具类库,通过静态方法封装,降低相关API的学习成本,提高工作效率,使Ja

dromara 25.1k Dec 30, 2022
Set level of characters in-game

SetLevel Basics This is a simple plugin to easily set character level in game. There will probably be issues if you don't use the commands as intended

Thoronium 15 Dec 16, 2022
Vert.x PoC for Project Loom Support

Vert.x Loom Wrapper Codegen This project contains a proof of concept implementation for a codegen wrapper API that provides virtual async-await suppor

Johannes Schüth 13 Oct 10, 2022
A flexible JSON/YAML linter for creating automated style guides, with baked in support for OpenAPI v2 & v3.

Astrum A flexible JSON/YAML linter for creating automated style guides, with baked in support for OpenAPI v2 & v3. OpenAPI is now a widely-adopted met

Apiwiz 11 May 24, 2022
Messenger(Chat app) is a real time one to one chat application with Push Notifications made using Firebase...

Messenger If you want a APK then create an issue, i'll send the link.. ChatApp is a real time one to one chat application with Push Notifications made

Tales 14 Apr 30, 2022
null 5 Jan 11, 2022
Weld, including integrations for Servlet containers and Java SE, examples and documentation

Weld Weld is the reference implementation of CDI: Contexts and Dependency Injection for the Java EE Platform which is the Java standard for dependency

Weld 345 Oct 28, 2022
A Java serialization/deserialization library to convert Java Objects into JSON and back

Gson Gson is a Java library that can be used to convert Java Objects into their JSON representation. It can also be used to convert a JSON string to a

Google 21.7k Jan 8, 2023
A universal types-preserving Java serialization library that can convert arbitrary Java Objects into JSON and back

A universal types-preserving Java serialization library that can convert arbitrary Java Objects into JSON and back, with a transparent support of any kind of self-references and with a full Java 9 compatibility.

Andrey Mogilev 9 Dec 30, 2021
Java with functions is a small java tools and utils library.

Java with functions is a small java tools and utils library.

null 4 Oct 14, 2022
Convert Java to JSON. Convert JSON to Java. Pretty print JSON. Java JSON serializer.

json-io Perfect Java serialization to and from JSON format (available on Maven Central). To include in your project: <dependency> <groupId>com.cedar

John DeRegnaucourt 303 Dec 30, 2022
Android processing and secured library for managing SharedPreferences as key-value elements efficiently and structurally.

Memo Android processing and secured library for managing SharedPreferences as key-value elements efficiently and structurally. 1. Depend on our librar

ZeoFlow 18 Jun 30, 2022
A JSON Transmission Protocol and an ORM Library for automatically providing APIs and Docs.

?? 零代码、热更新、全自动 ORM 库,后端接口和文档零代码,前端(客户端) 定制返回 JSON 的数据和结构。 ?? A JSON Transmission Protocol and an ORM Library for automatically providing APIs and Docs.

Tencent 14.4k Dec 31, 2022
A modern JSON library for Kotlin and Java.

Moshi Moshi is a modern JSON library for Android and Java. It makes it easy to parse JSON into Java objects: String json = ...; Moshi moshi = new Mos

Square 8.7k Dec 31, 2022
Generate Java types from JSON or JSON Schema and annotates those types for data-binding with Jackson, Gson, etc

jsonschema2pojo jsonschema2pojo generates Java types from JSON Schema (or example JSON) and can annotate those types for data-binding with Jackson 2.x

Joe Littlejohn 5.9k Jan 5, 2023
A JSON Schema validation implementation in pure Java, which aims for correctness and performance, in that order

Read me first The current version of this project is licensed under both LGPLv3 (or later) and ASL 2.0. The old version (2.0.x) was licensed under LGP

Java Json Tools 1.5k Jan 4, 2023
A modern and lightweight library for working with email addresses in Java

JMail A modern, fast, zero-dependency library for working with email addresses and performing email address validation in Java. Built for Java 8 and u

Rohan Nagar 67 Dec 22, 2022
Generate getter and setter on Java with ease! PS: Intended to use for labs where typing them are still the norm!!

GenerateGetterSetter Generate getter and setter on Java with ease! PS: Intended to use for labs where typing them are still the norm!! How to use Clon

Vishnu Sanal. T 4 Jan 4, 2022