Browse Source

Custom serializers accessors (#10658)

* add accessor methods for CUSTOM_SERIALIZERS in JsonCodec

* javadoc

* removed removeCustomSerializer method, renamed putCustomSerializer to addCustomSerializer, added sanity checks and JavaDocs

* refactored addCustomJsonSerializer to set, added test UI

* move enums to be parsed after custom serializers

* move adding custom serializer to static block

* throw an exception if multiple serializers are registered for class

* updated javadocs

* changed CustomJSONSerializerTest to a SingleBrowserTest

* moved CustomJSONSerializerTest to server/ and it's now not a browser test

* removed CustomJSONSerializerTest
tags/8.8.0.beta1
Olli Tietäväinen 5 years ago
parent
commit
9d5bdaf89b

+ 59
- 6
server/src/main/java/com/vaadin/server/JsonCodec.java View File

@@ -325,14 +325,14 @@ public class JsonCodec implements Serializable {
} else if (JsonValue.class
.isAssignableFrom(getClassForType(targetType))) {
return value;
} else if (Enum.class.isAssignableFrom(getClassForType(targetType))) {
Class<?> classForType = getClassForType(targetType);
return decodeEnum(classForType.asSubclass(Enum.class),
(JsonString) value);
} else if (CUSTOM_SERIALIZERS
.containsKey(getClassForType(targetType))) {
return CUSTOM_SERIALIZERS.get(getClassForType(targetType))
.deserialize(targetType, value, connectorTracker);
} else if (Enum.class.isAssignableFrom(getClassForType(targetType))) {
Class<?> classForType = getClassForType(targetType);
return decodeEnum(classForType.asSubclass(Enum.class),
(JsonString) value);
} else {
return decodeObject(targetType, (JsonObject) value,
connectorTracker);
@@ -445,6 +445,59 @@ public class JsonCodec implements Serializable {
throw new JsonException("Unknown type " + transportType);
}

/**
* Set a custom JSONSerializer for a specific Class. Existence of custom
* serializers is checked after basic types (Strings, Booleans, Numbers,
* Characters), Collections and Maps, so setting custom serializers for
* these won't have any effect.
* <p>
* To remove a previously set serializer, call this method with the second
* parameter set to {@code null}.
* <p>
* Custom serializers should only be added from static initializers or other
* places that are guaranteed to run only once. Trying to add a serializer
* to a class that already has one will cause an exception.
* <p>
* Warning: removing existing custom serializers may lead into unexpected
* behavior in components that expect the customized data. The framework's
* custom serializers are loaded in the static initializer block of this
* class.
*
* @see DateSerializer
* @throws IllegalArgumentException
* Thrown if parameter clazz is null.
* @throws IllegalStateException
* Thrown if serializer for parameter clazz is already
* registered and parameter jsonSerializer is not null.
* @param clazz
* The target class.
* @param jsonSerializer
* Custom JSONSerializer to add. If {@code null}, remove custom
* serializer from class clazz.
*/
public static <TYPE> void setCustomSerializer(Class<TYPE> clazz,
JSONSerializer<TYPE> jsonSerializer) {
if (clazz == null) {
throw new IllegalArgumentException(
"Cannot add serializer for null");
}
if (jsonSerializer == null) {
CUSTOM_SERIALIZERS.remove(clazz);
} else {
if (CUSTOM_SERIALIZERS.containsKey(clazz)) {
String err = String.format(
"Class %s already has a custom serializer. "
+ "This exception can be thrown if you try to "
+ "add a serializer from a non-static context. "
+ "Try using a static block instead.",
clazz.getName());
throw new IllegalStateException(err);
}
CUSTOM_SERIALIZERS.put(clazz, jsonSerializer);
}

}

private static UidlValue decodeUidlValue(JsonArray encodedJsonValue,
ConnectorTracker connectorTracker) {
String type = encodedJsonValue.getString(0);
@@ -668,10 +721,10 @@ public class JsonCodec implements Serializable {
}
// Connectors are simply serialized as ID.
toReturn = Json.create(((Connector) value).getConnectorId());
} else if (value instanceof Enum) {
toReturn = Json.create(((Enum<?>) value).name());
} else if (CUSTOM_SERIALIZERS.containsKey(value.getClass())) {
toReturn = serializeJson(value, connectorTracker);
} else if (value instanceof Enum) {
toReturn = Json.create(((Enum<?>) value).name());
} else if (valueType instanceof GenericArrayType) {
toReturn = encodeArrayContents(
((GenericArrayType) valueType).getGenericComponentType(),

+ 52
- 0
server/src/test/java/com/vaadin/server/CustomJSONSerializerTest.java View File

@@ -0,0 +1,52 @@
package com.vaadin.server;

import static org.junit.Assert.assertTrue;

import java.lang.reflect.Type;

import org.junit.Test;

import com.vaadin.server.communication.JSONSerializer;
import com.vaadin.ui.ConnectorTracker;

import elemental.json.JsonValue;

public class CustomJSONSerializerTest {
public static class Foo {

}

public static class FooSerializer implements JSONSerializer<Foo> {

@Override
public Foo deserialize(Type type, JsonValue jsonValue,
ConnectorTracker connectorTracker) {
return null;
}

@Override
public JsonValue serialize(Foo value,
ConnectorTracker connectorTracker) {
return null;
}

}

@Test
public void testMultipleRegistration() {
boolean thrown = false;
try {
JsonCodec.setCustomSerializer(Foo.class, new FooSerializer());
JsonCodec.setCustomSerializer(Foo.class, new FooSerializer());
} catch (IllegalStateException ise) {
thrown = true;
} finally {
JsonCodec.setCustomSerializer(Foo.class, null);
}
assertTrue("Multiple serializer registrations for one class "
+ "should throw an IllegalStateException", thrown);

}

}

+ 63
- 0
uitest/src/main/java/com/vaadin/tests/application/CustomJSONSerializer.java View File

@@ -0,0 +1,63 @@
package com.vaadin.tests.application;

import java.lang.reflect.Type;

import com.vaadin.server.JsonCodec;
import com.vaadin.server.VaadinRequest;
import com.vaadin.server.communication.JSONSerializer;
import com.vaadin.shared.communication.URLReference;
import com.vaadin.tests.components.AbstractTestUI;
import com.vaadin.ui.Button;
import com.vaadin.ui.ConnectorTracker;
import com.vaadin.ui.LoginForm;

import elemental.json.Json;
import elemental.json.JsonObject;
import elemental.json.JsonValue;

public class CustomJSONSerializer extends AbstractTestUI {

static {
JsonCodec.setCustomSerializer(URLReference.class,
new JSONSerializer<URLReference>() {

@Override
public URLReference deserialize(Type type,
JsonValue jsonValue,
ConnectorTracker connectorTracker) {
// NOP
return null;
}

@Override
public JsonValue serialize(URLReference value,
ConnectorTracker connectorTracker) {
JsonObject result = Json.createObject();
String url = value.getURL();
// change all test.com urls to vaadin.com
if ("http://www.test.com".equals(url)) {
url = "http://www.vaadin.com";
}
result.put("uRL", url);
return result;
}

});
}

public static class MyLoginForm extends LoginForm {
public void setResource(URLReference ref) {
getState().loginResource = ref;
}
}

@Override
protected void setup(VaadinRequest request) {
MyLoginForm loginForm = new MyLoginForm();
URLReference url = new URLReference();
url.setURL("http://www.test.com");
loginForm.setResource(url);
addComponent(loginForm);
}

}

Loading…
Cancel
Save