github.com/phrase/openapi@v0.0.0-20240514140800-49e8a106740e/openapi-generator/templates/java/CustomInstantDeserializer.mustache (about) 1 package {{invokerPackage}}; 2 3 import com.fasterxml.jackson.core.JsonParser; 4 import com.fasterxml.jackson.core.JsonTokenId; 5 import com.fasterxml.jackson.databind.DeserializationContext; 6 import com.fasterxml.jackson.databind.DeserializationFeature; 7 import com.fasterxml.jackson.databind.JsonDeserializer; 8 import com.fasterxml.jackson.datatype.threetenbp.DecimalUtils; 9 import com.fasterxml.jackson.datatype.threetenbp.deser.ThreeTenDateTimeDeserializerBase; 10 import com.fasterxml.jackson.datatype.threetenbp.function.BiFunction; 11 import com.fasterxml.jackson.datatype.threetenbp.function.Function; 12 import org.threeten.bp.DateTimeException; 13 import org.threeten.bp.DateTimeUtils; 14 import org.threeten.bp.Instant; 15 import org.threeten.bp.OffsetDateTime; 16 import org.threeten.bp.ZoneId; 17 import org.threeten.bp.ZonedDateTime; 18 import org.threeten.bp.format.DateTimeFormatter; 19 import org.threeten.bp.temporal.Temporal; 20 import org.threeten.bp.temporal.TemporalAccessor; 21 22 import java.io.IOException; 23 import java.math.BigDecimal; 24 25 /** 26 * Deserializer for ThreeTen temporal {@link Instant}s, {@link OffsetDateTime}, and {@link ZonedDateTime}s. 27 * Adapted from the jackson threetenbp InstantDeserializer to add support for deserializing rfc822 format. 28 * 29 * @author Nick Williams 30 */ 31 public class CustomInstantDeserializer<T extends Temporal> 32 extends ThreeTenDateTimeDeserializerBase<T> { 33 private static final long serialVersionUID = 1L; 34 35 public static final CustomInstantDeserializer<Instant> INSTANT = new CustomInstantDeserializer<Instant>( 36 Instant.class, DateTimeFormatter.ISO_INSTANT, 37 new Function<TemporalAccessor, Instant>() { 38 @Override 39 public Instant apply(TemporalAccessor temporalAccessor) { 40 return Instant.from(temporalAccessor); 41 } 42 }, 43 new Function<FromIntegerArguments, Instant>() { 44 @Override 45 public Instant apply(FromIntegerArguments a) { 46 return Instant.ofEpochMilli(a.value); 47 } 48 }, 49 new Function<FromDecimalArguments, Instant>() { 50 @Override 51 public Instant apply(FromDecimalArguments a) { 52 return Instant.ofEpochSecond(a.integer, a.fraction); 53 } 54 }, 55 null 56 ); 57 58 public static final CustomInstantDeserializer<OffsetDateTime> OFFSET_DATE_TIME = new CustomInstantDeserializer<OffsetDateTime>( 59 OffsetDateTime.class, DateTimeFormatter.ISO_OFFSET_DATE_TIME, 60 new Function<TemporalAccessor, OffsetDateTime>() { 61 @Override 62 public OffsetDateTime apply(TemporalAccessor temporalAccessor) { 63 return OffsetDateTime.from(temporalAccessor); 64 } 65 }, 66 new Function<FromIntegerArguments, OffsetDateTime>() { 67 @Override 68 public OffsetDateTime apply(FromIntegerArguments a) { 69 return OffsetDateTime.ofInstant(Instant.ofEpochMilli(a.value), a.zoneId); 70 } 71 }, 72 new Function<FromDecimalArguments, OffsetDateTime>() { 73 @Override 74 public OffsetDateTime apply(FromDecimalArguments a) { 75 return OffsetDateTime.ofInstant(Instant.ofEpochSecond(a.integer, a.fraction), a.zoneId); 76 } 77 }, 78 new BiFunction<OffsetDateTime, ZoneId, OffsetDateTime>() { 79 @Override 80 public OffsetDateTime apply(OffsetDateTime d, ZoneId z) { 81 return d.withOffsetSameInstant(z.getRules().getOffset(d.toLocalDateTime())); 82 } 83 } 84 ); 85 86 public static final CustomInstantDeserializer<ZonedDateTime> ZONED_DATE_TIME = new CustomInstantDeserializer<ZonedDateTime>( 87 ZonedDateTime.class, DateTimeFormatter.ISO_ZONED_DATE_TIME, 88 new Function<TemporalAccessor, ZonedDateTime>() { 89 @Override 90 public ZonedDateTime apply(TemporalAccessor temporalAccessor) { 91 return ZonedDateTime.from(temporalAccessor); 92 } 93 }, 94 new Function<FromIntegerArguments, ZonedDateTime>() { 95 @Override 96 public ZonedDateTime apply(FromIntegerArguments a) { 97 return ZonedDateTime.ofInstant(Instant.ofEpochMilli(a.value), a.zoneId); 98 } 99 }, 100 new Function<FromDecimalArguments, ZonedDateTime>() { 101 @Override 102 public ZonedDateTime apply(FromDecimalArguments a) { 103 return ZonedDateTime.ofInstant(Instant.ofEpochSecond(a.integer, a.fraction), a.zoneId); 104 } 105 }, 106 new BiFunction<ZonedDateTime, ZoneId, ZonedDateTime>() { 107 @Override 108 public ZonedDateTime apply(ZonedDateTime zonedDateTime, ZoneId zoneId) { 109 return zonedDateTime.withZoneSameInstant(zoneId); 110 } 111 } 112 ); 113 114 protected final Function<FromIntegerArguments, T> fromMilliseconds; 115 116 protected final Function<FromDecimalArguments, T> fromNanoseconds; 117 118 protected final Function<TemporalAccessor, T> parsedToValue; 119 120 protected final BiFunction<T, ZoneId, T> adjust; 121 122 protected CustomInstantDeserializer(Class<T> supportedType, 123 DateTimeFormatter parser, 124 Function<TemporalAccessor, T> parsedToValue, 125 Function<FromIntegerArguments, T> fromMilliseconds, 126 Function<FromDecimalArguments, T> fromNanoseconds, 127 BiFunction<T, ZoneId, T> adjust) { 128 super(supportedType, parser); 129 this.parsedToValue = parsedToValue; 130 this.fromMilliseconds = fromMilliseconds; 131 this.fromNanoseconds = fromNanoseconds; 132 this.adjust = adjust == null ? new BiFunction<T, ZoneId, T>() { 133 @Override 134 public T apply(T t, ZoneId zoneId) { 135 return t; 136 } 137 } : adjust; 138 } 139 140 @SuppressWarnings("unchecked") 141 protected CustomInstantDeserializer(CustomInstantDeserializer<T> base, DateTimeFormatter f) { 142 super((Class<T>) base.handledType(), f); 143 parsedToValue = base.parsedToValue; 144 fromMilliseconds = base.fromMilliseconds; 145 fromNanoseconds = base.fromNanoseconds; 146 adjust = base.adjust; 147 } 148 149 @Override 150 protected JsonDeserializer<T> withDateFormat(DateTimeFormatter dtf) { 151 if (dtf == _formatter) { 152 return this; 153 } 154 return new CustomInstantDeserializer<T>(this, dtf); 155 } 156 157 @Override 158 public T deserialize(JsonParser parser, DeserializationContext context) throws IOException { 159 //NOTE: Timestamps contain no timezone info, and are always in configured TZ. Only 160 //string values have to be adjusted to the configured TZ. 161 switch (parser.getCurrentTokenId()) { 162 case JsonTokenId.ID_NUMBER_FLOAT: { 163 BigDecimal value = parser.getDecimalValue(); 164 long seconds = value.longValue(); 165 int nanoseconds = DecimalUtils.extractNanosecondDecimal(value, seconds); 166 return fromNanoseconds.apply(new FromDecimalArguments( 167 seconds, nanoseconds, getZone(context))); 168 } 169 170 case JsonTokenId.ID_NUMBER_INT: { 171 long timestamp = parser.getLongValue(); 172 if (context.isEnabled(DeserializationFeature.READ_DATE_TIMESTAMPS_AS_NANOSECONDS)) { 173 return this.fromNanoseconds.apply(new FromDecimalArguments( 174 timestamp, 0, this.getZone(context) 175 )); 176 } 177 return this.fromMilliseconds.apply(new FromIntegerArguments( 178 timestamp, this.getZone(context) 179 )); 180 } 181 182 case JsonTokenId.ID_STRING: { 183 String string = parser.getText().trim(); 184 if (string.length() == 0) { 185 return null; 186 } 187 if (string.endsWith("+0000")) { 188 string = string.substring(0, string.length() - 5) + "Z"; 189 } 190 T value; 191 try { 192 TemporalAccessor acc = _formatter.parse(string); 193 value = parsedToValue.apply(acc); 194 if (context.isEnabled(DeserializationFeature.ADJUST_DATES_TO_CONTEXT_TIME_ZONE)) { 195 return adjust.apply(value, this.getZone(context)); 196 } 197 } catch (DateTimeException e) { 198 throw _peelDTE(e); 199 } 200 return value; 201 } 202 } 203 throw context.mappingException("Expected type float, integer, or string."); 204 } 205 206 private ZoneId getZone(DeserializationContext context) { 207 // Instants are always in UTC, so don't waste compute cycles 208 return (_valueClass == Instant.class) ? null : DateTimeUtils.toZoneId(context.getTimeZone()); 209 } 210 211 private static class FromIntegerArguments { 212 public final long value; 213 public final ZoneId zoneId; 214 215 private FromIntegerArguments(long value, ZoneId zoneId) { 216 this.value = value; 217 this.zoneId = zoneId; 218 } 219 } 220 221 private static class FromDecimalArguments { 222 public final long integer; 223 public final int fraction; 224 public final ZoneId zoneId; 225 226 private FromDecimalArguments(long integer, int fraction, ZoneId zoneId) { 227 this.integer = integer; 228 this.fraction = fraction; 229 this.zoneId = zoneId; 230 } 231 } 232 }