Skip to content

Commit

Permalink
[jsonb] JsonB support use of JsonObject for @Json.Unmapped type (#315)
Browse files Browse the repository at this point in the history
So Unmapped type can either be a Map<String,Object> or a JsonObject.
  • Loading branch information
rbygrave authored Dec 13, 2024
1 parent fe3c62b commit 0035381
Show file tree
Hide file tree
Showing 4 changed files with 79 additions and 13 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package org.example.customer.node;

import io.avaje.json.node.JsonObject;
import io.avaje.jsonb.Json;

@Json
public record HelloWithUnmapped(
String name,
int count,
@Json.Unmapped
JsonObject other) {
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
package org.example.customer.node;

import io.avaje.json.node.JsonObject;
import io.avaje.jsonb.JsonType;
import io.avaje.jsonb.Jsonb;
import org.junit.jupiter.api.Test;

import static org.assertj.core.api.Assertions.assertThat;

class HelloWithUnmappedTest {

Jsonb jsonb = Jsonb.builder().build();
JsonType<HelloWithUnmapped> jsonType = jsonb.type(HelloWithUnmapped.class);

@Test
void test() {
var source = new HelloWithUnmapped("hi", 3, JsonObject.create().add("extra", "b").add("extra2", 54L));

String asJson = jsonType.toJson(source);
assertThat(asJson).isEqualTo("{\"name\":\"hi\",\"count\":3,\"extra\":\"b\",\"extra2\":54}");

HelloWithUnmapped fromJson = jsonType.fromJson(asJson);
assertThat(fromJson).isEqualTo(source);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -462,7 +462,11 @@ private void writeFromJsonImplementation(Append writer, String varName) {
writer.eol().append(" String type = null;").eol();
}
if (unmappedField != null) {
writer.append(" Map<String, Object> unmapped = new LinkedHashMap<>();").eol();
if (unmappedJsonNodeType()) {
writer.append(" var unmapped = io.avaje.json.node.JsonObject.create();").eol();
} else {
writer.append(" var unmapped = new java.util.LinkedHashMap<String, Object>();").eol();
}
}
writeFromJsonSwitch(writer, directLoad, varName);
writer.eol();
Expand All @@ -480,6 +484,10 @@ private void writeFromJsonImplementation(Append writer, String varName) {
writer.append(" }").eol();
}

private boolean unmappedJsonNodeType() {
return unmappedField.type().topType().startsWith("io.avaje.json.node.");
}

private void writeJsonBuildResult(Append writer, String varName) {
writer.append(" // build and return %s", shortName).eol();
if (constructor == null) {
Expand Down Expand Up @@ -618,8 +626,13 @@ private void writeFromJsonSwitch(Append writer, boolean defaultConstructor, Stri
writer.append(" default:").eol();
final String unmappedFieldName = caseInsensitiveKeys ? "origFieldName" : "fieldName";
if (unmappedField != null) {
writer.append(" Object value = objectJsonAdapter.fromJson(reader);").eol();
writer.append(" unmapped.put(%s, value);", unmappedFieldName).eol();
if (unmappedJsonNodeType()) {
writer.append(" var value = jsonNodeAdapter.fromJson(reader);").eol();
writer.append(" unmapped.add(%s, value);", unmappedFieldName).eol();
} else {
writer.append(" var value = objectJsonAdapter.fromJson(reader);").eol();
writer.append(" unmapped.put(%s, value);", unmappedFieldName).eol();
}
} else {
writer.append(" reader.unmappedField(%s);", unmappedFieldName).eol();
writer.append(" reader.skipValue();").eol();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -62,9 +62,15 @@ final class FieldProperty {
adapterFieldName = "rawAdapter";
defaultValue = "null";
} else if (unmapped) {
genericType = GenericType.parse("java.lang.Object");
adapterShortType = "JsonAdapter<Object>";
adapterFieldName = "objectJsonAdapter";
if (unmappedJsonObject()) {
genericType = GenericType.parse("io.avaje.json.node.JsonNode");
adapterShortType = "JsonAdapter<JsonNode>";
adapterFieldName = "jsonNodeAdapter";
} else {
genericType = GenericType.parse("java.lang.Object");
adapterShortType = "JsonAdapter<Object>";
adapterFieldName = "objectJsonAdapter";
}
defaultValue = "null";
} else {
genericType = GenericType.parse(rawType);
Expand All @@ -80,6 +86,10 @@ final class FieldProperty {
}
}

private boolean unmappedJsonObject() {
return rawType.startsWith("io.avaje.json.node");
}

void setConstructorParam() {
constructorParam = true;
}
Expand Down Expand Up @@ -180,9 +190,9 @@ private boolean nameHasIsPrefix() {
}

void addImports(Set<String> importTypes) {
customSerializer.ifPresent(t -> importTypes.add(t.toString()));
if (unmapped) {
importTypes.add("java.util.*");
customSerializer.ifPresent(importTypes::add);
if (unmapped && unmappedJsonObject()) {
importTypes.add("io.avaje.json.node.JsonNode");
}
if (!raw) {
genericType.addImports(importTypes);
Expand Down Expand Up @@ -232,13 +242,19 @@ private String genericTypeReplacement(String asType, String replaceWith) {

void writeToJson(Append writer, String varName, String prefix) {
if (unmapped) {
writer.append("%sMap<String, Object> unmapped = ", prefix);
writer.append("%svar unmapped = ", prefix);
writeGetValue(writer, varName, ";");
writer.eol();
writer.append("%sif (unmapped != null) {", prefix).eol();
writer.append("%s for (Map.Entry<String, Object> entry : unmapped.entrySet()) {", prefix).eol();
writer.append("%s writer.name(entry.getKey());", prefix).eol();
writer.append("%s objectJsonAdapter.toJson(writer, entry.getValue());", prefix).eol();
if (unmappedJsonObject()) {
writer.append("%s for (var entry : unmapped.elements().entrySet()) {", prefix).eol();
writer.append("%s writer.name(entry.getKey());", prefix).eol();
writer.append("%s jsonNodeAdapter.toJson(writer, entry.getValue());", prefix).eol();
} else {
writer.append("%s for (var entry : unmapped.entrySet()) {", prefix).eol();
writer.append("%s writer.name(entry.getKey());", prefix).eol();
writer.append("%s objectJsonAdapter.toJson(writer, entry.getValue());", prefix).eol();
}
writer.append("%s }", prefix).eol();
writer.append("%s}", prefix).eol();
} else {
Expand Down

0 comments on commit 0035381

Please sign in to comment.