Skip to content

Commit 6936b7c

Browse files
javier-godoypaodb
authored andcommitted
feat: initial implementation
1 parent a95d2c4 commit 6936b7c

File tree

6 files changed

+333
-6
lines changed

6 files changed

+333
-6
lines changed

README.md

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -6,15 +6,11 @@
66

77
# Json Migration Helper
88

9-
This is a template project for building new Vaadin 24 add-ons
9+
Provides a compatibility layer for JSON handling to abstract away breaking changes introduced in Vaadin version 25.
1010

1111
## Features
1212

13-
* List the features of your add-on in here
14-
15-
## Online demo
16-
17-
[Online demo here](http://addonsv24.flowingcode.com/json-migration-helper)
13+
Detects the runtime version and uses version-specific helpers to ensure that code calling its methods does not need to be aware of underlying Vaadin API changes.
1814

1915
## Download release
2016

pom.xml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,12 @@
6868
<artifactId>vaadin-core</artifactId>
6969
<optional>true</optional>
7070
</dependency>
71+
<dependency>
72+
<groupId>tools.jackson.core</groupId>
73+
<artifactId>jackson-databind</artifactId>
74+
<version>3.0.0</version>
75+
<optional>true</optional>
76+
</dependency>
7177
<dependency>
7278
<groupId>org.projectlombok</groupId>
7379
<artifactId>lombok</artifactId>
Lines changed: 108 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,108 @@
1+
/*-
2+
* #%L
3+
* Json Migration Helper
4+
* %%
5+
* Copyright (C) 2025 Flowing Code
6+
* %%
7+
* Licensed under the Apache License, Version 2.0 (the "License");
8+
* you may not use this file except in compliance with the License.
9+
* You may obtain a copy of the License at
10+
*
11+
* http://www.apache.org/licenses/LICENSE-2.0
12+
*
13+
* Unless required by applicable law or agreed to in writing, software
14+
* distributed under the License is distributed on an "AS IS" BASIS,
15+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16+
* See the License for the specific language governing permissions and
17+
* limitations under the License.
18+
* #L%
19+
*/
20+
package com.flowingcode.vaadin.jsonmigration;
21+
22+
import java.lang.reflect.Method;
23+
import com.vaadin.flow.dom.Element;
24+
import com.vaadin.flow.server.Version;
25+
import elemental.json.JsonValue;
26+
import lombok.SneakyThrows;
27+
28+
/**
29+
* Provides a compatibility layer for JSON handling to abstract away breaking changes
30+
* introduced in Vaadin version 25.
31+
* <p>
32+
* This utility class detects the runtime version and uses version-specific helpers
33+
* to ensure that code calling its methods does not need to be aware of underlying
34+
* Vaadin API changes.
35+
*
36+
* @author Javier Godoy
37+
*/
38+
public class JsonMigration {
39+
40+
private static final JsonMigrationHelper helper = initializeHelper();
41+
42+
@SneakyThrows
43+
private static JsonMigrationHelper initializeHelper() {
44+
if (Version.getMajorVersion()>24) {
45+
Class<?> helperType = Class.forName(JsonMigration.class.getName()+"Helper25");
46+
return (JsonMigrationHelper) helperType.getConstructor().newInstance();
47+
} else {
48+
return new LegacyJsonMigrationHelper();
49+
}
50+
}
51+
52+
private static final Class<?> BASE_JSON_NODE = lookup_BaseJsonNode();
53+
54+
private static Class<?> lookup_BaseJsonNode() {
55+
try {
56+
return Class.forName("tools.jackson.databind.node.BaseJsonNode");
57+
} catch (ClassNotFoundException e) {
58+
return null;
59+
}
60+
}
61+
62+
/**
63+
* Converts a given Java object into a {@code JsonValue}.
64+
*
65+
* <p>This method delegates the conversion to a version-specific helper to handle
66+
* any differences in the serialization process.
67+
*
68+
* @param object the object to convert
69+
* @return the {@code JsonValue} representation of the object
70+
*/
71+
public static JsonValue convertToJsonValue(Object object) {
72+
return helper.convertToJsonValue(object);
73+
}
74+
75+
@SneakyThrows
76+
private static Object invoke(Method method, Object instance, Object... args) {
77+
return helper.invoke(method, instance, args);
78+
}
79+
80+
81+
private static Method Element_setPropertyJson = lookup_setPropertyJson();
82+
83+
@SneakyThrows
84+
private static Method lookup_setPropertyJson() {
85+
if (Version.getMajorVersion()>24) {
86+
return Element.class.getMethod("setPropertyJson", String.class, BASE_JSON_NODE);
87+
} else {
88+
return Element.class.getMethod("setPropertyJson", String.class, JsonValue.class);
89+
}
90+
}
91+
92+
/**
93+
* Sets a JSON-valued property on a given {@code Element}, transparently handling
94+
* version-specific method signatures.
95+
*
96+
* <p>This method uses reflection to call the appropriate {@code setPropertyJson} method
97+
* on the {@code Element} class, which has a different signature for its JSON
98+
* parameter in library versions before and after Vaadin 25.
99+
*
100+
* @param element the {@code Element} on which to set the property
101+
* @param name the name of the property to set
102+
* @param json the {@code JsonValue} to be set as the property's value
103+
*/
104+
public static void setPropertyJson(Element element, String name, JsonValue json) {
105+
invoke(Element_setPropertyJson, element, name, json);
106+
}
107+
108+
}
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
/*-
2+
* #%L
3+
* Json Migration Helper
4+
* %%
5+
* Copyright (C) 2025 Flowing Code
6+
* %%
7+
* Licensed under the Apache License, Version 2.0 (the "License");
8+
* you may not use this file except in compliance with the License.
9+
* You may obtain a copy of the License at
10+
*
11+
* http://www.apache.org/licenses/LICENSE-2.0
12+
*
13+
* Unless required by applicable law or agreed to in writing, software
14+
* distributed under the License is distributed on an "AS IS" BASIS,
15+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16+
* See the License for the specific language governing permissions and
17+
* limitations under the License.
18+
* #L%
19+
*/
20+
package com.flowingcode.vaadin.jsonmigration;
21+
22+
import elemental.json.JsonValue;
23+
24+
import java.lang.reflect.Method;
25+
import com.vaadin.flow.dom.Element;
26+
27+
interface JsonMigrationHelper {
28+
29+
JsonValue convertToJsonValue(Object object);
30+
31+
Object invoke(Method method, Object instance, Object... args);
32+
33+
}
Lines changed: 138 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,138 @@
1+
/*-
2+
* #%L
3+
* Json Migration Helper
4+
* %%
5+
* Copyright (C) 2025 Flowing Code
6+
* %%
7+
* Licensed under the Apache License, Version 2.0 (the "License");
8+
* you may not use this file except in compliance with the License.
9+
* You may obtain a copy of the License at
10+
*
11+
* http://www.apache.org/licenses/LICENSE-2.0
12+
*
13+
* Unless required by applicable law or agreed to in writing, software
14+
* distributed under the License is distributed on an "AS IS" BASIS,
15+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16+
* See the License for the specific language governing permissions and
17+
* limitations under the License.
18+
* #L%
19+
*/
20+
package com.flowingcode.vaadin.jsonmigration;
21+
22+
import java.lang.reflect.Method;
23+
import java.util.Arrays;
24+
import elemental.json.Json;
25+
import elemental.json.JsonArray;
26+
import elemental.json.JsonObject;
27+
import elemental.json.JsonValue;
28+
import lombok.NoArgsConstructor;
29+
import lombok.SneakyThrows;
30+
import tools.jackson.databind.JsonNode;
31+
import tools.jackson.databind.node.ArrayNode;
32+
import tools.jackson.databind.node.BaseJsonNode;
33+
import tools.jackson.databind.node.JsonNodeFactory;
34+
import tools.jackson.databind.node.ObjectNode;
35+
36+
@NoArgsConstructor
37+
class JsonMigrationHelper25 implements JsonMigrationHelper {
38+
39+
@Override
40+
public JsonValue convertToJsonValue(Object object) {
41+
if (object instanceof JsonValue) {
42+
return (JsonValue) object;
43+
} else if (object instanceof JsonNode) {
44+
return convertToJsonValue((JsonNode) object);
45+
} else if (object == null) {
46+
return null;
47+
} else {
48+
throw new ClassCastException(
49+
object.getClass().getName() + " cannot be converted to elemental.json.JsonObject");
50+
}
51+
}
52+
53+
@Override
54+
@SneakyThrows
55+
public Object invoke(Method method, Object instance, Object... args) {
56+
Object[] convertedArgs = null;
57+
Class<?> parameterTypes[] = method.getParameterTypes();
58+
for (int i = 0; i < parameterTypes.length; i++) {
59+
if (args[i] instanceof JsonValue && parameterTypes[i] == BaseJsonNode.class) {
60+
61+
if (convertedArgs == null) {
62+
convertedArgs = Arrays.copyOf(args, args.length);
63+
}
64+
convertedArgs[i] = convertToJsonNode((JsonValue) args[i]);
65+
}
66+
}
67+
if (convertedArgs == null) {
68+
convertedArgs = args;
69+
}
70+
return method.invoke(instance, convertedArgs);
71+
}
72+
73+
private static JsonValue convertToJsonValue(JsonNode jsonNode) {
74+
switch (jsonNode.getNodeType()) {
75+
case OBJECT:
76+
JsonObject jsonObject = Json.createObject();
77+
JsonObject source = (JsonObject)jsonNode;
78+
for (String key : source.keys()) {
79+
jsonObject.put(key, convertToJsonValue(source.get(key)));
80+
}
81+
return jsonObject;
82+
case ARRAY:
83+
JsonArray jsonArray = Json.createArray();
84+
for (int i = 0; i < jsonNode.size(); i++) {
85+
jsonArray.set(i, convertToJsonValue(jsonNode.get(i)));
86+
}
87+
return jsonArray;
88+
case STRING:
89+
return Json.create(jsonNode.asText());
90+
case NUMBER:
91+
return Json.create(jsonNode.asDouble());
92+
case BOOLEAN:
93+
return Json.create(jsonNode.asBoolean());
94+
case NULL:
95+
return Json.createNull();
96+
default:
97+
throw new IllegalArgumentException("Unsupported JsonNode type: " + jsonNode.getNodeType());
98+
}
99+
}
100+
101+
private static final JsonNodeFactory nodeFactory = JsonNodeFactory.instance;
102+
103+
private static BaseJsonNode convertToJsonNode(JsonValue jsonValue) {
104+
switch (jsonValue.getType()) {
105+
case OBJECT:
106+
JsonObject jsonObject = (JsonObject) jsonValue;
107+
ObjectNode objectNode = nodeFactory.objectNode();
108+
for (String key : jsonObject.keys()) {
109+
objectNode.set(key, convertToJsonNode(jsonObject.get(key)));
110+
}
111+
return objectNode;
112+
113+
case ARRAY:
114+
JsonArray jsonArray = (JsonArray) jsonValue;
115+
ArrayNode arrayNode = nodeFactory.arrayNode(jsonArray.length());
116+
for (int i = 0; i < jsonArray.length(); i++) {
117+
arrayNode.set(i, convertToJsonNode(jsonArray.get(i)));
118+
}
119+
return arrayNode;
120+
121+
case STRING:
122+
return nodeFactory.textNode(jsonValue.asString());
123+
124+
case NUMBER:
125+
return nodeFactory.numberNode(jsonValue.asNumber());
126+
127+
case BOOLEAN:
128+
return nodeFactory.booleanNode(jsonValue.asBoolean());
129+
130+
case NULL:
131+
return nodeFactory.nullNode();
132+
133+
default:
134+
throw new IllegalArgumentException("Unsupported JsonValue type: " + jsonValue.getType());
135+
}
136+
}
137+
138+
}
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
/*-
2+
* #%L
3+
* Json Migration Helper
4+
* %%
5+
* Copyright (C) 2025 Flowing Code
6+
* %%
7+
* Licensed under the Apache License, Version 2.0 (the "License");
8+
* you may not use this file except in compliance with the License.
9+
* You may obtain a copy of the License at
10+
*
11+
* http://www.apache.org/licenses/LICENSE-2.0
12+
*
13+
* Unless required by applicable law or agreed to in writing, software
14+
* distributed under the License is distributed on an "AS IS" BASIS,
15+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16+
* See the License for the specific language governing permissions and
17+
* limitations under the License.
18+
* #L%
19+
*/
20+
package com.flowingcode.vaadin.jsonmigration;
21+
22+
import java.lang.reflect.Method;
23+
import elemental.json.JsonValue;
24+
import lombok.NoArgsConstructor;
25+
import lombok.SneakyThrows;
26+
27+
@NoArgsConstructor
28+
class LegacyJsonMigrationHelper implements JsonMigrationHelper {
29+
30+
@Override
31+
public JsonValue convertToJsonValue(Object object) {
32+
if (object instanceof JsonValue) {
33+
return (JsonValue) object;
34+
} else {
35+
throw new ClassCastException(
36+
object.getClass().getName() + " cannot be converted to elemental.json.JsonObject");
37+
}
38+
}
39+
40+
@Override
41+
@SneakyThrows
42+
public Object invoke(Method method, Object instance, Object... args) {
43+
return method.invoke(instance, args);
44+
}
45+
46+
}

0 commit comments

Comments
 (0)