Skip to content

Commit 8d108ac

Browse files
authored
feat: ReactAdapter and element to support jackson (#21111)
* feat: ReactAdapter and element to support jackson Have ReactAdapter and elemnt use jackson. AbstractSinglePropertyField to support jackson type. part of #20741 * Enable Jackson for EventData * add client callable eventdata jackson test DeaultRpcDecoder now handles elemental and jackson * Remove elemental from scrollOptions * Minor cleanup * Simplify data object creation.
1 parent 64ee593 commit 8d108ac

File tree

16 files changed

+2969
-42
lines changed

16 files changed

+2969
-42
lines changed

flow-react/src/main/java/com/vaadin/flow/component/react/ReactAdapterComponent.java

+70-17
Original file line numberDiff line numberDiff line change
@@ -16,24 +16,25 @@
1616
package com.vaadin.flow.component.react;
1717

1818
import com.fasterxml.jackson.core.type.TypeReference;
19+
import com.fasterxml.jackson.databind.JsonNode;
20+
import com.fasterxml.jackson.databind.node.BaseJsonNode;
1921

2022
import com.vaadin.flow.component.Component;
2123
import com.vaadin.flow.dom.DomListenerRegistration;
2224
import com.vaadin.flow.dom.Element;
2325
import com.vaadin.flow.function.SerializableConsumer;
2426
import com.vaadin.flow.function.SerializableFunction;
25-
import com.vaadin.flow.internal.JsonCodec;
26-
import com.vaadin.flow.internal.JsonUtils;
27+
import com.vaadin.flow.internal.JacksonCodec;
28+
import com.vaadin.flow.internal.JacksonUtils;
2729

28-
import com.vaadin.flow.internal.StateNode;
30+
import com.vaadin.flow.internal.JsonUtils;
2931
import com.vaadin.flow.internal.nodefeature.NodeProperties;
3032

31-
import elemental.json.Json;
32-
import elemental.json.JsonValue;
33-
3433
import java.util.HashMap;
3534
import java.util.Map;
3635

36+
import elemental.json.JsonValue;
37+
3738
/**
3839
* An abstract implementation of an adapter for integrating with React
3940
* components. To be used together with a React adapter Web Component that
@@ -110,7 +111,7 @@ protected <T> DomListenerRegistration addStateChangeListener(
110111
* value to assign
111112
*/
112113
protected void setState(String stateName, Object value) {
113-
getElement().setPropertyJson(stateName, writeAsJson(value));
114+
getElement().setPropertyJson(stateName, writeToJson(value));
114115
}
115116

116117
/**
@@ -153,12 +154,30 @@ protected <T> T getState(String stateName, TypeReference<T> typeReference) {
153154
* @return converted object instance
154155
* @param <T>
155156
* type of result instance
157+
* @deprecated use {@link #readFromJson(JsonNode, Class)} instead
156158
*/
159+
@Deprecated
157160
protected static <T> T readFromJson(JsonValue jsonValue,
158161
Class<T> typeClass) {
159162
return JsonUtils.readValue(jsonValue, typeClass);
160163
}
161164

165+
/**
166+
* Converts JsonValue into Java object of given type.
167+
*
168+
* @param jsonValue
169+
* JSON value to convert, not {@code null}
170+
* @param typeClass
171+
* type class of converted object instance
172+
* @return converted object instance
173+
* @param <T>
174+
* type of result instance
175+
*/
176+
protected static <T> T readFromJson(JsonNode jsonValue,
177+
Class<T> typeClass) {
178+
return JacksonUtils.readValue(jsonValue, typeClass);
179+
}
180+
162181
/**
163182
* Converts JsonValue into Java object of given type.
164183
*
@@ -169,23 +188,54 @@ protected static <T> T readFromJson(JsonValue jsonValue,
169188
* @return converted object instance
170189
* @param <T>
171190
* type of result instance
191+
* @deprecated use {@link #readFromJson(JsonNode, TypeReference)} instead
172192
*/
193+
@Deprecated
173194
protected static <T> T readFromJson(JsonValue jsonValue,
174195
TypeReference<T> typeReference) {
175196
return JsonUtils.readValue(jsonValue, typeReference);
176197
}
177198

199+
/**
200+
* Converts JsonValue into Java object of given type.
201+
*
202+
* @param jsonValue
203+
* JSON value to convert, not {@code null}
204+
* @param typeReference
205+
* type reference of converted object instance
206+
* @return converted object instance
207+
* @param <T>
208+
* type of result instance
209+
*/
210+
protected static <T> T readFromJson(JsonNode jsonValue,
211+
TypeReference<T> typeReference) {
212+
return JacksonUtils.readValue(jsonValue, typeReference);
213+
}
214+
178215
/**
179216
* Converts Java object into JsonValue.
180217
*
181218
* @param object
182219
* Java object to convert
183220
* @return converted JSON value
221+
* @deprecated use {@link #writeToJson(Object)}
184222
*/
223+
@Deprecated
185224
protected static JsonValue writeAsJson(Object object) {
186225
return JsonUtils.writeValue(object);
187226
}
188227

228+
/**
229+
* Converts Java object into JsonValue.
230+
*
231+
* @param object
232+
* Java object to convert
233+
* @return converted JSON value
234+
*/
235+
protected static BaseJsonNode writeToJson(Object object) {
236+
return JacksonUtils.writeValue(object);
237+
}
238+
189239
/**
190240
* Get the Flow container element that is set up in React template for given
191241
* name attribute.
@@ -210,29 +260,32 @@ protected Element getContentElement(String name) {
210260
return contentMap.get(name);
211261
}
212262

213-
private JsonValue getPropertyJson(String propertyName) {
263+
private JsonNode getPropertyJson(String propertyName) {
214264
var rawValue = getElement().getPropertyRaw(propertyName);
215265
if (rawValue == null) {
216-
return Json.createNull();
217-
} else if (rawValue instanceof JsonValue jsonValue) {
218-
return jsonValue;
266+
return JacksonUtils.nullNode();
267+
} else if (rawValue instanceof JsonNode jsonNode) {
268+
return jsonNode;
219269
} else if (rawValue instanceof String stringValue) {
220-
return Json.create(stringValue);
270+
return JacksonUtils.createNode(stringValue);
221271
} else if (rawValue instanceof Double doubleValue) {
222-
return Json.create(doubleValue);
272+
return JacksonUtils.createNode(doubleValue);
223273
} else if (rawValue instanceof Boolean booleanValue) {
224-
return Json.create(booleanValue);
274+
return JacksonUtils.createNode(booleanValue);
275+
} else if (rawValue instanceof JsonValue jsonValue) {
276+
// TODO: remove when elemental dropped
277+
return JacksonUtils.mapElemental(jsonValue);
225278
} else {
226-
return Json.create(rawValue.toString());
279+
return JacksonUtils.createNode(rawValue.toString());
227280
}
228281
}
229282

230283
private <T> DomListenerRegistration addJsonReaderStateChangeListener(
231-
String stateName, SerializableFunction<JsonValue, T> jsonReader,
284+
String stateName, SerializableFunction<JsonNode, T> jsonReader,
232285
SerializableConsumer<T> listener) {
233286
return getElement().addPropertyChangeListener(stateName,
234287
stateName + "-changed", (event -> {
235-
JsonValue newStateJson = JsonCodec
288+
JsonNode newStateJson = JacksonCodec
236289
.encodeWithoutTypeInfo(event.getValue());
237290
T newState = jsonReader.apply(newStateJson);
238291
listener.accept(newState);

flow-react/src/main/java/com/vaadin/flow/component/react/ReactRouterOutlet.java

-2
Original file line numberDiff line numberDiff line change
@@ -18,8 +18,6 @@
1818

1919
import com.vaadin.flow.component.Tag;
2020
import com.vaadin.flow.component.dependency.JsModule;
21-
import com.vaadin.flow.component.react.ReactAdapterComponent;
22-
import com.vaadin.flow.router.Route;
2321

2422
/**
2523
* Component used to create a React {@code Outlet} element for binding a Hilla

flow-server/src/main/java/com/vaadin/flow/component/AbstractSinglePropertyField.java

+21-1
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
import java.util.Objects;
2222
import java.util.stream.Collectors;
2323

24+
import com.fasterxml.jackson.databind.node.BaseJsonNode;
2425
import com.googlecode.gentyref.GenericTypeReflector;
2526
import com.vaadin.flow.dom.DomListenerRegistration;
2627
import com.vaadin.flow.dom.Element;
@@ -29,6 +30,7 @@
2930
import com.vaadin.flow.function.SerializableBiConsumer;
3031
import com.vaadin.flow.function.SerializableBiFunction;
3132
import com.vaadin.flow.function.SerializableFunction;
33+
import com.vaadin.flow.internal.JacksonCodec;
3234
import com.vaadin.flow.internal.JsonCodec;
3335
import com.vaadin.flow.shared.util.SharedUtil;
3436

@@ -128,6 +130,8 @@ private <C extends AbstractField<C, V>, V> SerializableBiConsumer<C, V> createWr
128130
Boolean.FALSE);
129131
addHandler(Element::setProperty, Element::getProperty, Integer.class,
130132
Integer.valueOf(0));
133+
typeHandlers.put(BaseJsonNode.class,
134+
getJsonHandler(BaseJsonNode.class));
131135
typeHandlers.put(JsonValue.class, getHandler(JsonValue.class));
132136
}
133137

@@ -254,7 +258,10 @@ public <P> AbstractSinglePropertyField(String propertyName, T defaultValue,
254258
@SuppressWarnings({ "unchecked", "rawtypes" })
255259
private <P> TypeHandler<P> findHandler(Class<P> clazz) {
256260
TypeHandler<P> typeHandler = (TypeHandler<P>) typeHandlers.get(clazz);
257-
if (typeHandler == null && JsonValue.class.isAssignableFrom(clazz)) {
261+
if (typeHandler == null && BaseJsonNode.class.isAssignableFrom(clazz)) {
262+
typeHandler = getJsonHandler((Class) clazz);
263+
} else if (typeHandler == null
264+
&& JsonValue.class.isAssignableFrom(clazz)) {
258265
typeHandler = getHandler((Class) clazz);
259266
}
260267
if (typeHandler == null) {
@@ -366,6 +373,7 @@ protected void setPresentationValue(T newPresentationValue) {
366373
propertyWriter.accept((C) this, newPresentationValue);
367374
}
368375

376+
@Deprecated
369377
private static <P extends JsonValue> TypeHandler<P> getHandler(
370378
Class<P> type) {
371379
ElementGetter<P> getter = (element, property, defaultValue) -> {
@@ -379,6 +387,18 @@ private static <P extends JsonValue> TypeHandler<P> getHandler(
379387
return new TypeHandler<P>(setter, getter, null);
380388
}
381389

390+
private static <P extends BaseJsonNode> TypeHandler<P> getJsonHandler(
391+
Class<P> type) {
392+
ElementGetter<P> getter = (element, property, defaultValue) -> {
393+
Serializable value = element.getPropertyRaw(property);
394+
// JsonValue is passed straight through, other primitive
395+
// values are jsonified
396+
return type.cast(JacksonCodec.encodeWithoutTypeInfo(value));
397+
};
398+
ElementSetter<P> setter = Element::setPropertyJson;
399+
return new TypeHandler<P>(setter, getter, null);
400+
}
401+
382402
private static <T> void addHandler(ElementSetter<T> setter,
383403
ElementGetter<T> getter, Class<T> type, T typeDefaultValue) {
384404
typeHandlers.put(type,

flow-server/src/main/java/com/vaadin/flow/component/ComponentEventBus.java

+13
Original file line numberDiff line numberDiff line change
@@ -28,16 +28,21 @@
2828
import java.util.Optional;
2929
import java.util.function.Consumer;
3030

31+
import com.fasterxml.jackson.databind.JsonNode;
32+
3133
import com.vaadin.flow.dom.DebouncePhase;
3234
import com.vaadin.flow.dom.DisabledUpdateMode;
3335
import com.vaadin.flow.dom.DomEvent;
3436
import com.vaadin.flow.dom.DomListenerRegistration;
3537
import com.vaadin.flow.dom.Element;
3638
import com.vaadin.flow.internal.AnnotationReader;
39+
import com.vaadin.flow.internal.JacksonCodec;
40+
import com.vaadin.flow.internal.JacksonUtils;
3741
import com.vaadin.flow.internal.JsonCodec;
3842
import com.vaadin.flow.shared.Registration;
3943

4044
import elemental.json.Json;
45+
import elemental.json.JsonNull;
4146
import elemental.json.JsonValue;
4247

4348
/**
@@ -361,6 +366,14 @@ private List<Object> createEventDataObjects(DomEvent domEvent,
361366
|| type == Element.class) {
362367
eventDataObjects.add(parseStateNodeIdToComponentReference(
363368
domEvent, type, expression));
369+
} else if (JsonNode.class.isAssignableFrom(type)) {
370+
// TODO: Decode and remove if when domEvent uses jackson.
371+
JsonValue eventValue = domEvent.getEventData().get(expression);
372+
if (eventValue == null || eventValue instanceof JsonNull) {
373+
eventDataObjects.add(null);
374+
} else {
375+
eventDataObjects.add(JacksonUtils.mapElemental(eventValue));
376+
}
364377
} else {
365378
JsonValue jsonValue = domEvent.getEventData().get(expression);
366379
if (jsonValue == null) {

flow-server/src/main/java/com/vaadin/flow/component/ScrollOptions.java

+4-3
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,9 @@
33
import java.io.Serializable;
44
import java.util.Locale;
55

6-
import elemental.json.Json;
7-
import elemental.json.JsonObject;
6+
import com.fasterxml.jackson.databind.node.ObjectNode;
7+
8+
import com.vaadin.flow.internal.JacksonUtils;
89

910
/**
1011
* Options for scrollIntoView.
@@ -123,7 +124,7 @@ public Alignment getInline() {
123124
* @return a json object as a string
124125
*/
125126
public String toJson() {
126-
JsonObject json = Json.createObject();
127+
ObjectNode json = JacksonUtils.createObjectNode();
127128
if (behavior != Behavior.AUTO) {
128129
json.put("behavior", behavior.name().toLowerCase(Locale.ENGLISH));
129130
}

flow-server/src/main/java/com/vaadin/flow/component/UI.java

+11-4
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727
import java.util.concurrent.Future;
2828
import java.util.stream.Stream;
2929

30+
import com.fasterxml.jackson.databind.JsonNode;
3031
import org.slf4j.Logger;
3132
import org.slf4j.LoggerFactory;
3233

@@ -44,6 +45,7 @@
4445
import com.vaadin.flow.i18n.LocaleChangeEvent;
4546
import com.vaadin.flow.internal.CurrentInstance;
4647
import com.vaadin.flow.internal.ExecutionContext;
48+
import com.vaadin.flow.internal.JacksonUtils;
4749
import com.vaadin.flow.internal.StateNode;
4850
import com.vaadin.flow.internal.StateTree.ExecutionRegistration;
4951
import com.vaadin.flow.internal.nodefeature.ElementData;
@@ -87,6 +89,8 @@
8789
import com.vaadin.flow.server.communication.PushConnection;
8890
import com.vaadin.flow.shared.Registration;
8991

92+
import elemental.json.Json;
93+
import elemental.json.JsonObject;
9094
import elemental.json.JsonValue;
9195

9296
/**
@@ -1743,7 +1747,7 @@ public static class BrowserNavigateEvent extends ComponentEvent<UI> {
17431747
private final String route;
17441748
private final String query;
17451749
private final String appShellTitle;
1746-
private final JsonValue historyState;
1750+
private final JsonNode historyState;
17471751
private final String trigger;
17481752

17491753
/**
@@ -1766,7 +1770,7 @@ public BrowserNavigateEvent(UI source, boolean fromClient,
17661770
@EventData("route") String route,
17671771
@EventData("query") String query,
17681772
@EventData("appShellTitle") String appShellTitle,
1769-
@EventData("historyState") JsonValue historyState,
1773+
@EventData("historyState") JsonNode historyState,
17701774
@EventData("trigger") String trigger) {
17711775
super(source, true);
17721776
this.route = route;
@@ -1838,7 +1842,8 @@ public BrowserRefreshEvent(UI source, boolean fromClient,
18381842
public void connectClient(String flowRoutePath, String flowRouteQuery,
18391843
String appShellTitle, JsonValue historyState, String trigger) {
18401844
browserNavigate(new BrowserNavigateEvent(this, false, flowRoutePath,
1841-
flowRouteQuery, appShellTitle, historyState, trigger));
1845+
flowRouteQuery, appShellTitle,
1846+
JacksonUtils.mapElemental(historyState), trigger));
18421847
}
18431848

18441849
/**
@@ -1879,9 +1884,11 @@ public void browserNavigate(BrowserNavigateEvent event) {
18791884
} else {
18801885
History.HistoryStateChangeHandler handler = getPage().getHistory()
18811886
.getHistoryStateChangeHandler();
1887+
JsonObject state = event.historyState == null ? null
1888+
: Json.parse(event.historyState.toString());
18821889
handler.onHistoryStateChange(
18831890
new History.HistoryStateChangeEvent(getPage().getHistory(),
1884-
event.historyState, location, navigationTrigger));
1891+
state, location, navigationTrigger));
18851892
}
18861893

18871894
// true if the target is client-view and the push mode is disable

0 commit comments

Comments
 (0)