mirror of
https://github.com/danieldemus/openhab-core.git
synced 2025-01-10 13:21:53 +01:00
GsonBuilder: Explicitly set date format (#4185)
Between Java 17 and Java 21, serialization of DateTime has changed due to CLDR 42 which uses a narrow non-breaking space. To ease switching JDK versions, the seralization format is explicitly set to the Java 17 format. Signed-off-by: Holger Friedrich <mail@holger-friedrich.de>
This commit is contained in:
parent
bad043ff12
commit
fa9cff6be9
@ -39,6 +39,7 @@ import org.openhab.core.auth.client.oauth2.AccessTokenResponse;
|
||||
import org.openhab.core.auth.client.oauth2.OAuthException;
|
||||
import org.openhab.core.auth.client.oauth2.OAuthResponseException;
|
||||
import org.openhab.core.io.net.http.HttpClientFactory;
|
||||
import org.openhab.core.library.types.DateTimeType;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
@ -83,7 +84,8 @@ public class OAuthConnector {
|
||||
public OAuthConnector(HttpClientFactory httpClientFactory, @Nullable Fields extraFields, GsonBuilder gsonBuilder) {
|
||||
this.httpClientFactory = httpClientFactory;
|
||||
this.extraFields = extraFields;
|
||||
gson = gsonBuilder.setFieldNamingPolicy(FieldNamingPolicy.LOWER_CASE_WITH_UNDERSCORES)
|
||||
gson = gsonBuilder.setDateFormat(DateTimeType.DATE_PATTERN_JSON_COMPAT)
|
||||
.setFieldNamingPolicy(FieldNamingPolicy.LOWER_CASE_WITH_UNDERSCORES)
|
||||
.registerTypeAdapter(Instant.class, (JsonDeserializer<Instant>) (json, typeOfT, context) -> {
|
||||
try {
|
||||
return Instant.parse(json.getAsString());
|
||||
|
@ -35,6 +35,7 @@ import org.eclipse.jdt.annotation.Nullable;
|
||||
import org.openhab.core.auth.client.oauth2.AccessTokenResponse;
|
||||
import org.openhab.core.auth.client.oauth2.StorageCipher;
|
||||
import org.openhab.core.auth.oauth2client.internal.cipher.SymmetricKeyCipher;
|
||||
import org.openhab.core.library.types.DateTimeType;
|
||||
import org.openhab.core.storage.Storage;
|
||||
import org.openhab.core.storage.StorageService;
|
||||
import org.osgi.service.component.annotations.Activate;
|
||||
@ -238,7 +239,7 @@ public class OAuthStoreHandlerImpl implements OAuthStoreHandler {
|
||||
public StorageFacade(Storage<String> storage) {
|
||||
this.storage = storage;
|
||||
// Add adapters for Instant
|
||||
gson = new GsonBuilder()
|
||||
gson = new GsonBuilder().setDateFormat(DateTimeType.DATE_PATTERN_JSON_COMPAT)
|
||||
.registerTypeAdapter(Instant.class, (JsonDeserializer<Instant>) (json, typeOfT, context) -> {
|
||||
try {
|
||||
return Instant.parse(json.getAsString());
|
||||
|
@ -26,6 +26,7 @@ import org.openhab.core.config.core.ConfigurationDeserializer;
|
||||
import org.openhab.core.config.core.ConfigurationSerializer;
|
||||
import org.openhab.core.config.core.OrderingMapSerializer;
|
||||
import org.openhab.core.config.core.OrderingSetSerializer;
|
||||
import org.openhab.core.library.types.DateTimeType;
|
||||
|
||||
import com.google.gson.Gson;
|
||||
import com.google.gson.GsonBuilder;
|
||||
@ -44,6 +45,7 @@ public abstract class AbstractGSONParser<T> implements Parser<T> {
|
||||
|
||||
// A Gson instance to use by the parsers
|
||||
protected static Gson gson = new GsonBuilder() //
|
||||
.setDateFormat(DateTimeType.DATE_PATTERN_JSON_COMPAT) //
|
||||
.registerTypeAdapter(CompositeActionType.class, new ActionInstanceCreator()) //
|
||||
.registerTypeAdapter(CompositeConditionType.class, new ConditionInstanceCreator()) //
|
||||
.registerTypeAdapter(CompositeTriggerType.class, new TriggerInstanceCreator()) //
|
||||
|
@ -30,6 +30,7 @@ import javax.ws.rs.ext.MessageBodyReader;
|
||||
import javax.ws.rs.ext.MessageBodyWriter;
|
||||
|
||||
import org.openhab.core.io.rest.RESTConstants;
|
||||
import org.openhab.core.library.types.DateTimeType;
|
||||
import org.osgi.service.component.annotations.Component;
|
||||
import org.osgi.service.jaxrs.whiteboard.JaxrsWhiteboardConstants;
|
||||
import org.osgi.service.jaxrs.whiteboard.propertytypes.JaxrsApplicationSelect;
|
||||
@ -61,7 +62,7 @@ public class MediaTypeExtension<T> implements MessageBodyReader<T>, MessageBodyW
|
||||
* Constructor.
|
||||
*/
|
||||
public MediaTypeExtension() {
|
||||
final Gson gson = new GsonBuilder().create();
|
||||
final Gson gson = new GsonBuilder().setDateFormat(DateTimeType.DATE_PATTERN_JSON_COMPAT).create();
|
||||
readers.put(mediaTypeWithoutParams(MediaType.APPLICATION_JSON_TYPE), new GsonMessageBodyReader<>(gson));
|
||||
readers.put(mediaTypeWithoutParams(MediaType.TEXT_PLAIN_TYPE), new PlainMessageBodyReader<>());
|
||||
writers.put(mediaTypeWithoutParams(MediaType.APPLICATION_JSON_TYPE), new GsonMessageBodyWriter<>(gson));
|
||||
|
@ -23,6 +23,7 @@ import java.util.stream.Stream;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.openhab.core.library.types.DateTimeType;
|
||||
|
||||
import com.google.gson.Gson;
|
||||
import com.google.gson.GsonBuilder;
|
||||
@ -35,7 +36,7 @@ import com.google.gson.GsonBuilder;
|
||||
@NonNullByDefault
|
||||
public class Stream2JSONInputStreamTest {
|
||||
|
||||
private static final Gson GSON = new GsonBuilder().create();
|
||||
private static final Gson GSON = new GsonBuilder().setDateFormat(DateTimeType.DATE_PATTERN_JSON_COMPAT).create();
|
||||
|
||||
@Test
|
||||
public void shouldReturnForEmptyStream() throws Exception {
|
||||
|
@ -37,6 +37,7 @@ import org.openhab.core.config.core.Configuration;
|
||||
import org.openhab.core.config.core.ConfigurationDeserializer;
|
||||
import org.openhab.core.config.core.OrderingMapSerializer;
|
||||
import org.openhab.core.config.core.OrderingSetSerializer;
|
||||
import org.openhab.core.library.types.DateTimeType;
|
||||
import org.openhab.core.storage.Storage;
|
||||
import org.openhab.core.storage.json.internal.migration.TypeMigrationException;
|
||||
import org.openhab.core.storage.json.internal.migration.TypeMigrator;
|
||||
@ -104,12 +105,14 @@ public class JsonStorage<T> implements Storage<T> {
|
||||
this.typeMigrators = typeMigrators.stream().collect(Collectors.toMap(TypeMigrator::getOldType, e -> e));
|
||||
|
||||
this.internalMapper = new GsonBuilder() //
|
||||
.setDateFormat(DateTimeType.DATE_PATTERN_JSON_COMPAT) //
|
||||
.registerTypeHierarchyAdapter(Map.class, new OrderingMapSerializer())//
|
||||
.registerTypeHierarchyAdapter(Set.class, new OrderingSetSerializer())//
|
||||
.registerTypeHierarchyAdapter(Map.class, new StorageEntryMapDeserializer()) //
|
||||
.setPrettyPrinting() //
|
||||
.create();
|
||||
this.entityMapper = new GsonBuilder() //
|
||||
.setDateFormat(DateTimeType.DATE_PATTERN_JSON_COMPAT) //
|
||||
.registerTypeHierarchyAdapter(Map.class, new OrderingMapSerializer())//
|
||||
.registerTypeHierarchyAdapter(Set.class, new OrderingSetSerializer())//
|
||||
.registerTypeAdapter(Configuration.class, new ConfigurationDeserializer()) //
|
||||
|
@ -20,7 +20,11 @@ import java.math.BigDecimal;
|
||||
import java.net.MalformedURLException;
|
||||
import java.net.URL;
|
||||
import java.nio.file.Files;
|
||||
import java.time.Instant;
|
||||
import java.time.ZoneId;
|
||||
import java.time.ZonedDateTime;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Date;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.LinkedList;
|
||||
@ -31,7 +35,10 @@ import java.util.Set;
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.condition.EnabledForJreRange;
|
||||
import org.junit.jupiter.api.condition.JRE;
|
||||
import org.openhab.core.config.core.Configuration;
|
||||
import org.openhab.core.library.types.DateTimeType;
|
||||
import org.openhab.core.test.java.JavaTest;
|
||||
|
||||
import com.google.gson.Gson;
|
||||
@ -171,7 +178,7 @@ public class JsonStorageTest extends JavaTest {
|
||||
}
|
||||
String storageStringReserialized = Files.readString(tmpFile.toPath());
|
||||
assertEquals(storageStringAB, storageStringReserialized);
|
||||
Gson gson = new GsonBuilder().create();
|
||||
Gson gson = new GsonBuilder().setDateFormat(DateTimeType.DATE_PATTERN_JSON_COMPAT).create();
|
||||
|
||||
// Parse json. Gson preserves json object key ordering when we parse only JsonObject
|
||||
JsonObject orderedMap = gson.fromJson(storageStringAB, JsonObject.class);
|
||||
@ -212,6 +219,46 @@ public class JsonStorageTest extends JavaTest {
|
||||
.keySet().toArray());
|
||||
}
|
||||
|
||||
@Test
|
||||
@EnabledForJreRange(max = JRE.JAVA_19)
|
||||
public void testDateSerialization17() {
|
||||
// do NOT set format, setDateFormat(DateTimeType.DATE_PATTERN_JSON_COMPAT)!
|
||||
Gson gson = new GsonBuilder().create();
|
||||
|
||||
// generate a Date instance for 1-Jan-1980 0:00, compensating local time zone
|
||||
ZonedDateTime zdt = ZonedDateTime.of(1980, 1, 1, 0, 0, 0, 0, ZoneId.systemDefault());
|
||||
Date date = Date.from(Instant.ofEpochSecond(zdt.toEpochSecond()));
|
||||
// \u20af will encode to 3 bytes, 0xe280af
|
||||
assertEquals("\"Jan 1, 1980, 12:00:00 AM\"", gson.toJson(date));
|
||||
}
|
||||
|
||||
// CLDR 42 introduced in Java 20 changes serialization of Date, adding a narrow non-breakable space
|
||||
@Test
|
||||
@EnabledForJreRange(min = JRE.JAVA_20)
|
||||
public void testDateSerialization21() {
|
||||
// do NOT set format, setDateFormat(DateTimeType.DATE_PATTERN_JSON_COMPAT)!
|
||||
Gson gson = new GsonBuilder().create();
|
||||
|
||||
// generate a Date instance for 1-Jan-1980 0:00, compensating local time zone
|
||||
ZonedDateTime zdt = ZonedDateTime.of(1980, 1, 1, 0, 0, 0, 0, ZoneId.systemDefault());
|
||||
Date date = Date.from(Instant.ofEpochSecond(zdt.toEpochSecond()));
|
||||
|
||||
// \u20af will encode to 3 bytes, 0xe280af
|
||||
assertEquals("\"Jan 1, 1980, 12:00:00\u202fAM\"", gson.toJson(date));
|
||||
}
|
||||
|
||||
// workaround, should work with Java 17 and 21
|
||||
@Test
|
||||
public void testDateSerialization() {
|
||||
Gson gson = new GsonBuilder().setDateFormat(DateTimeType.DATE_PATTERN_JSON_COMPAT).create();
|
||||
|
||||
// generate a Date instance for 1-Jan-1980 0:00, compensating local time zone
|
||||
ZonedDateTime zdt = ZonedDateTime.of(1980, 1, 1, 0, 0, 0, 0, ZoneId.systemDefault());
|
||||
Date date = Date.from(Instant.ofEpochSecond(zdt.toEpochSecond()));
|
||||
|
||||
assertEquals("\"Jan 1, 1980, 12:00:00 AM\"", gson.toJson(date));
|
||||
}
|
||||
|
||||
private static class DummyObject {
|
||||
|
||||
// For the test here we use Linked variants of Map and Set which preserve the insertion order
|
||||
|
@ -30,6 +30,7 @@ import org.junit.jupiter.params.provider.Arguments;
|
||||
import org.junit.jupiter.params.provider.MethodSource;
|
||||
import org.openhab.core.config.core.OrderingMapSerializer;
|
||||
import org.openhab.core.config.core.OrderingSetSerializer;
|
||||
import org.openhab.core.library.types.DateTimeType;
|
||||
import org.openhab.core.storage.json.internal.migration.PersistedTransformationTypeMigrator;
|
||||
import org.openhab.core.storage.json.internal.migration.TypeMigrationException;
|
||||
import org.openhab.core.storage.json.internal.migration.TypeMigrator;
|
||||
@ -47,8 +48,9 @@ import com.google.gson.JsonElement;
|
||||
public class PersistedTransformationMigratorTest {
|
||||
|
||||
private final Gson internalMapper = new GsonBuilder() //
|
||||
.registerTypeHierarchyAdapter(Map.class, new OrderingMapSerializer())//
|
||||
.registerTypeHierarchyAdapter(Set.class, new OrderingSetSerializer())//
|
||||
.setDateFormat(DateTimeType.DATE_PATTERN_JSON_COMPAT) //
|
||||
.registerTypeHierarchyAdapter(Map.class, new OrderingMapSerializer()) //
|
||||
.registerTypeHierarchyAdapter(Set.class, new OrderingSetSerializer()) //
|
||||
.registerTypeHierarchyAdapter(Map.class, new StorageEntryMapDeserializer()) //
|
||||
.setPrettyPrinting() //
|
||||
.create();
|
||||
|
@ -31,6 +31,7 @@ import org.junit.jupiter.params.provider.Arguments;
|
||||
import org.junit.jupiter.params.provider.MethodSource;
|
||||
import org.openhab.core.config.core.OrderingMapSerializer;
|
||||
import org.openhab.core.config.core.OrderingSetSerializer;
|
||||
import org.openhab.core.library.types.DateTimeType;
|
||||
import org.openhab.core.storage.json.internal.migration.BridgeImplTypeMigrator;
|
||||
import org.openhab.core.storage.json.internal.migration.ThingImplTypeMigrator;
|
||||
import org.openhab.core.storage.json.internal.migration.TypeMigrationException;
|
||||
@ -49,8 +50,9 @@ import com.google.gson.JsonElement;
|
||||
public class ThingStorageEntityMigratorTest {
|
||||
|
||||
private final Gson internalMapper = new GsonBuilder() //
|
||||
.registerTypeHierarchyAdapter(Map.class, new OrderingMapSerializer())//
|
||||
.registerTypeHierarchyAdapter(Set.class, new OrderingSetSerializer())//
|
||||
.setDateFormat(DateTimeType.DATE_PATTERN_JSON_COMPAT) //
|
||||
.registerTypeHierarchyAdapter(Map.class, new OrderingMapSerializer()) //
|
||||
.registerTypeHierarchyAdapter(Set.class, new OrderingSetSerializer()) //
|
||||
.registerTypeHierarchyAdapter(Map.class, new StorageEntryMapDeserializer()) //
|
||||
.setPrettyPrinting() //
|
||||
.create();
|
||||
|
@ -48,6 +48,8 @@ public class DateTimeType implements PrimitiveType, State, Command {
|
||||
public static final String DATE_PATTERN_WITH_TZ_AND_MS = "yyyy-MM-dd'T'HH:mm:ss.SSSZ";
|
||||
public static final String DATE_PATTERN_WITH_TZ_AND_MS_GENERAL = "yyyy-MM-dd'T'HH:mm:ss.SSSz";
|
||||
public static final String DATE_PATTERN_WITH_TZ_AND_MS_ISO = "yyyy-MM-dd'T'HH:mm:ss.SSSX";
|
||||
// serialization of Date, Java 17 compatible format
|
||||
public static final String DATE_PATTERN_JSON_COMPAT = "MMM d, yyyy, h:mm:ss aaa";
|
||||
|
||||
// internal patterns for parsing
|
||||
private static final String DATE_PARSE_PATTERN_WITHOUT_TZ = "yyyy-MM-dd'T'HH:mm"
|
||||
|
Loading…
Reference in New Issue
Block a user