Skip to content

Commit

Permalink
Handle enums that have true/false values as booleans (#275) (#276)
Browse files Browse the repository at this point in the history
Co-authored-by: Sylvain Wallez <sylvain@elastic.co>
  • Loading branch information
github-actions[bot] and swallez authored May 3, 2022
1 parent 6e824d0 commit 249412b
Show file tree
Hide file tree
Showing 4 changed files with 85 additions and 9 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,8 @@
import co.elastic.clients.json.JsonEnum;
import co.elastic.clients.json.JsonpDeserializable;
import co.elastic.clients.json.JsonpDeserializer;
import co.elastic.clients.json.JsonpMapper;
import jakarta.json.stream.JsonGenerator;

/**
*
Expand All @@ -52,5 +54,17 @@ public String jsonValue() {
return this.jsonValue;
}

public static final JsonEnum.Deserializer<Refresh> _DESERIALIZER = new JsonEnum.Deserializer<>(Refresh.values());
@Override
public void serialize(JsonGenerator generator, JsonpMapper params) {
if (this == Refresh.True) {
generator.write(true);
} else if (this == Refresh.False) {
generator.write(false);
} else {
generator.write(jsonValue());
}
}

public static final JsonEnum.Deserializer<Refresh> _DESERIALIZER = new JsonEnum.Deserializer.AllowingBooleans<>(
Refresh.values());
}
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,8 @@
import co.elastic.clients.json.JsonEnum;
import co.elastic.clients.json.JsonpDeserializable;
import co.elastic.clients.json.JsonpDeserializer;
import co.elastic.clients.json.JsonpMapper;
import jakarta.json.stream.JsonGenerator;

/**
*
Expand Down Expand Up @@ -55,6 +57,17 @@ public String jsonValue() {
return this.jsonValue;
}

public static final JsonEnum.Deserializer<DynamicMapping> _DESERIALIZER = new JsonEnum.Deserializer<>(
@Override
public void serialize(JsonGenerator generator, JsonpMapper params) {
if (this == DynamicMapping.True) {
generator.write(true);
} else if (this == DynamicMapping.False) {
generator.write(false);
} else {
generator.write(jsonValue());
}
}

public static final JsonEnum.Deserializer<DynamicMapping> _DESERIALIZER = new JsonEnum.Deserializer.AllowingBooleans<>(
DynamicMapping.values());
}
45 changes: 40 additions & 5 deletions java-client/src/main/java/co/elastic/clients/json/JsonEnum.java
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,8 @@
import java.util.Map;
import java.util.NoSuchElementException;

import static jakarta.json.stream.JsonParser.Event;

/**
* Base interface for enumerations in API types. Members have a JSON representation and also accept
* aliases when parsed from a string value.
Expand All @@ -47,13 +49,18 @@ default void serialize(JsonGenerator generator, JsonpMapper params) {
}

class Deserializer<T extends JsonEnum> extends JsonpDeserializerBase<T> {

private static final EnumSet<Event> acceptedEvents = EnumSet.of(Event.VALUE_STRING, Event.KEY_NAME);
private static final EnumSet<Event> nativeEvents = EnumSet.of(Event.VALUE_STRING);

private final Map<String, T> lookupTable;

public Deserializer(T[] values) {
super(
EnumSet.of(JsonParser.Event.VALUE_STRING, JsonParser.Event.KEY_NAME),
EnumSet.of(JsonParser.Event.VALUE_STRING)
);
this(values, acceptedEvents);
}

protected Deserializer(T[] values, EnumSet<Event> acceptedEvents) {
super(acceptedEvents, nativeEvents);

// Use the same size calculation as in java.lang.Enum.enumConstantDirectory
this.lookupTable = new HashMap<>((int)(values.length / 0.75f) + 1);
Expand All @@ -69,7 +76,7 @@ public Deserializer(T[] values) {
}

@Override
public T deserialize(JsonParser parser, JsonpMapper mapper, JsonParser.Event event) {
public T deserialize(JsonParser parser, JsonpMapper mapper, Event event) {
String value = parser.getString();
return deserialize(value, parser);
}
Expand Down Expand Up @@ -104,5 +111,33 @@ public T parse(String value) {
}
return result;
}

/**
* An enum deserializer that also accepts boolean values. Used for a few properties that started as two-state booleans
* and evolved into enums over time.
*/
public static class AllowingBooleans<T extends JsonEnum> extends Deserializer<T> {

private static final EnumSet<Event> acceptedEventsAndBoolean =
EnumSet.of(Event.VALUE_STRING, Event.KEY_NAME, Event.VALUE_TRUE, Event.VALUE_FALSE);

public AllowingBooleans(T[] values) {
super(values, acceptedEventsAndBoolean);
}

@Override
public T deserialize(JsonParser parser, JsonpMapper mapper, Event event) {
String value;
if (event == Event.VALUE_TRUE) {
value = "true";
} else if (event == Event.VALUE_FALSE) {
value = "false";
} else {
value = parser.getString();
}

return deserialize(value, parser);
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -20,13 +20,13 @@
package co.elastic.clients.elasticsearch.model;

import co.elastic.clients.elasticsearch._types.Bytes;
import co.elastic.clients.elasticsearch._types.Refresh;
import co.elastic.clients.elasticsearch._types.mapping.GeoOrientation;
import org.junit.Assert;
import org.junit.Test;

import java.util.Arrays;

public class EnumTest extends Assert {
public class EnumTest extends ModelTestCase {

@Test
public void testSimpleEnum() {
Expand All @@ -43,4 +43,18 @@ public void testEnumWithAliases() {
assertEquals(GeoOrientation.Right, GeoOrientation._DESERIALIZER.parse(alias));
});
}

@Test
public void testBooleanEnum() {
// Quoted value
assertEquals(Refresh.WaitFor, checkJsonRoundtrip(Refresh.WaitFor, "\"wait_for\""));

// Unquoted boolean values
assertEquals(Refresh.True, checkJsonRoundtrip(Refresh.True, "true"));
assertEquals(Refresh.False, checkJsonRoundtrip(Refresh.False, "false"));

// true/false as strings
assertEquals(Refresh.True, fromJson("\"true\"", Refresh.class));
assertEquals(Refresh.False, fromJson("\"false\"", Refresh.class));
}
}

0 comments on commit 249412b

Please sign in to comment.