Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add ZoneId converter #24

Merged
merged 2 commits into from
Jul 13, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ target
.idea/libraries/
.idea/scala_settings.xml
.idea/dictionaries/
*.ipr
*.iws

# Mac
.DS_Store
18 changes: 0 additions & 18 deletions gson-javatime-serialisers.iml

This file was deleted.

19 changes: 18 additions & 1 deletion src/main/java/com/fatboyindustrial/gsonjavatime/Converters.java
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@
import java.time.LocalTime;
import java.time.OffsetDateTime;
import java.time.OffsetTime;
import java.time.ZoneId;
import java.time.ZonedDateTime;

/**
Expand Down Expand Up @@ -62,6 +63,9 @@ public class Converters
/** The specific genericized type for {@code Instant}. */
public static final Type INSTANT_TYPE = new TypeToken<Instant>(){}.getType();

/** The specific genericized type for {@code ZoneId}. */
public static final Type ZONE_ID_TYPE = new TypeToken<ZoneId>(){}.getType();

/**
* Registers all the Java Time converters.
* @param builder The GSON builder to register the converters with.
Expand All @@ -78,6 +82,7 @@ public static GsonBuilder registerAll(GsonBuilder builder)
registerOffsetTime(builder);
registerZonedDateTime(builder);
registerInstant(builder);
registerZoneId(builder);

return builder;
}
Expand Down Expand Up @@ -162,7 +167,19 @@ public static GsonBuilder registerZonedDateTime(GsonBuilder builder)
public static GsonBuilder registerInstant(GsonBuilder builder)
{
builder.registerTypeAdapter(INSTANT_TYPE, new InstantConverter());

return builder;
}

/**
* Registers the {@link ZoneIdConverter} converter.
* @param builder The GSON builder to register the converter with.
* @return A reference to {@code builder}.
*/
public static GsonBuilder registerZoneId(GsonBuilder builder)
{
builder.registerTypeAdapter(ZONE_ID_TYPE, new ZoneIdConverter());

return builder;
}
}
100 changes: 100 additions & 0 deletions src/main/java/com/fatboyindustrial/gsonjavatime/ZoneIdConverter.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
/*
* Copyright 2014 Greg Kopff
* All rights reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall 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 com.fatboyindustrial.gsonjavatime;

import com.google.gson.JsonDeserializationContext;
import com.google.gson.JsonDeserializer;
import com.google.gson.JsonElement;
import com.google.gson.JsonParseException;
import com.google.gson.JsonPrimitive;
import com.google.gson.JsonSerializationContext;
import com.google.gson.JsonSerializer;

import java.lang.reflect.Type;
import java.time.ZoneId;

/**
* GSON serialiser/deserialiser for converting {@link ZoneId} objects.
*/
public class ZoneIdConverter implements JsonSerializer<ZoneId>, JsonDeserializer<ZoneId> {

/**
* Gson invokes this call-back method during serialization when it encounters a field of the
* specified type. <p>
*
* In the implementation of this call-back method, you should consider invoking
* {@link JsonSerializationContext#serialize(Object, Type)} method to create JsonElements for any
* non-trivial field of the {@code src} object. However, you should never invoke it on the
* {@code src} object itself since that will cause an infinite loop (Gson will call your
* call-back method again).
*
* @param src the object that needs to be converted to Json.
* @param typeOfSrc the actual type (fully genericized version) of the source object.
* @return a JsonElement corresponding to the specified object.
*/
@Override
public JsonElement serialize(final ZoneId src, final Type typeOfSrc, final JsonSerializationContext context)
{
if (src == null)
{
return null;
}
return new JsonPrimitive(src.getId());
}

/**
* Gson invokes this call-back method during deserialization when it encounters a field of the
* specified type. <p>
*
* In the implementation of this call-back method, you should consider invoking
* {@link JsonDeserializationContext#deserialize(JsonElement, Type)} method to create objects
* for any non-trivial field of the returned object. However, you should never invoke it on the
* the same type passing {@code json} since that will cause an infinite loop (Gson will call your
* call-back method again).
*
* @param json The Json data being deserialized
* @param typeOfT The type of the Object to deserialize to
* @return a deserialized object of the specified type typeOfT which is a subclass of {@code T}
* @throws JsonParseException if json is not in the expected format of {@code typeOfT}
*/
@Override
public ZoneId deserialize(final JsonElement json, final Type typeOfT, final JsonDeserializationContext context) throws
JsonParseException
{
if (json == null)
{
return null;
}
if (json.isJsonNull())
{
return null;
}
final String zoneIdentifier = json.getAsString();
if (zoneIdentifier==null || zoneIdentifier.isEmpty())
{
return null;
}
return ZoneId.of(zoneIdentifier);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,7 @@ public void testSerialisation() throws Exception
container.ot = OffsetTime.of(container.lt, ZoneOffset.ofHours(10));
container.zdt = ZonedDateTime.of(container.ld, container.lt, ZoneId.of("Australia/Brisbane"));
container.i = container.odt.toInstant();
container.zi = ZoneId.of("Australia/Brisbane");

final String jsonString = gson.toJson(container);
final JsonObject json = gson.fromJson(jsonString, JsonObject.class).getAsJsonObject();
Expand All @@ -74,6 +75,7 @@ public void testSerialisation() throws Exception
assertThat(json.get("ot").getAsString(), is("12:56:00+10:00"));
assertThat(json.get("zdt").getAsString(), is("1969-07-21T12:56:00+10:00[Australia/Brisbane]"));
assertThat(json.get("i").getAsString(), is("1969-07-21T02:56:00Z"));
assertThat(json.get("zi").getAsString(), is("Australia/Brisbane"));
}

/**
Expand All @@ -92,6 +94,7 @@ public void testDeserialisation() throws Exception
container.ot = OffsetTime.of(container.lt, ZoneOffset.ofHours(10));
container.zdt = ZonedDateTime.of(container.ld, container.lt, ZoneId.of("Australia/Brisbane"));
container.i = container.odt.toInstant();
container.zi = ZoneId.of("Australia/Brisbane");

final JsonObject serialized = new JsonObject();
serialized.add("ld", new JsonPrimitive("1969-07-21"));
Expand All @@ -101,6 +104,7 @@ public void testDeserialisation() throws Exception
serialized.add("ot", new JsonPrimitive("12:56:00+10:00"));
serialized.add("zdt", new JsonPrimitive("1969-07-21T12:56:00+10:00[Australia/Brisbane]"));
serialized.add("i", new JsonPrimitive("1969-07-21T02:56:00Z"));
serialized.add("zi", new JsonPrimitive("Australia/Brisbane"));

final String jsonString = gson.toJson(serialized);
final Container deserialised = gson.fromJson(jsonString, Container.class);
Expand All @@ -112,6 +116,7 @@ public void testDeserialisation() throws Exception
assertThat(deserialised.ot, is(container.ot));
assertThat(deserialised.zdt, is(container.zdt));
assertThat(deserialised.i, is(container.i));
assertThat(deserialised.zi, is(container.zi));
}

/**
Expand All @@ -126,5 +131,6 @@ private static class Container
private OffsetTime ot;
private ZonedDateTime zdt;
private Instant i;
private ZoneId zi;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
/*
* Copyright 2014 Greg Kopff
* All rights reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall 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 com.fatboyindustrial.gsonjavatime;

import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.google.gson.reflect.TypeToken;
import org.junit.Test;
import org.junit.Rule;
import org.junit.rules.ExpectedException;

import java.lang.reflect.Type;
import java.time.ZoneId;
import java.time.DateTimeException;

import static org.hamcrest.Matchers.is;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertThat;

/**
* Tests for {@link ZoneIdConverter}.
*/
public class ZoneIdConverterTest {

/** The specific genericized type for {@code ZoneId}. */
private static final Type ZONE_ID_TYPE = new TypeToken<ZoneId>(){}.getType();
@Rule
public ExpectedException exception = ExpectedException.none();

/**
* Tests that serialising to JSON works.
*/
@Test
public void testSerialisation() throws Exception
{
final Gson gson = registerZoneId(new GsonBuilder()).create();

final ZoneId zoneId = ZoneId.of("Australia/Brisbane");
final String json= gson.toJson(zoneId, ZoneId.class);
assertThat(json, is("\"Australia/Brisbane\""));
}

/**
* Tests that deserialising from JSON works.
*/
@Test
public void testDeserialisation() throws Exception
{
final Gson gson = registerZoneId(new GsonBuilder()).create();

final String json = "\"Australia/Brisbane\"";
final ZoneId zoneId = gson.fromJson(json, ZoneId.class);

assertThat(zoneId, is(ZoneId.of("Australia/Brisbane")));
}

/**
* Tests that deserialising from JSON works with an empty value.
*/
@Test
public void testDeserialisationWithEmptyValue() throws Exception
{
final Gson gson = registerZoneId(new GsonBuilder()).create();

final String json = "\"\"";
final ZoneId zoneId = gson.fromJson(json, ZoneId.class);

assertNull(zoneId);
}

/**
* Tests that deserialising from JSON works with a whitespace.
*/
@Test
public void testDeserialisationWithWhitespace() throws Exception
{
exception.expect(DateTimeException.class);
exception.expectMessage("Invalid ID for ZoneOffset, invalid format: ");

final Gson gson = registerZoneId(new GsonBuilder()).create();

final String json = "\" \"";
final ZoneId zoneId = gson.fromJson(json, ZoneId.class);
}

/**
* Registers the {@link ZoneIdConverter} converter.
* @param builder The GSON builder to register the converter with.
* @return A reference to {@code builder}.
*/
private static GsonBuilder registerZoneId(GsonBuilder builder)
{
builder.registerTypeAdapter(ZONE_ID_TYPE, new ZoneIdConverter());

return builder;
}
}