Skip to content

Commit c94db5e

Browse files
committed
JOHNZON-421 Defer start of object/array
This is necessary as we don't know what Serializers/Adapters/Converters actually do write. If we start an object with { and the JsonbSerializer just writes a string value (json primitive) then we will blow up. Deferring the start fixes this issue.
1 parent d536371 commit c94db5e

16 files changed

Lines changed: 582 additions & 122 deletions

File tree

johnzon-jsonb/src/main/java/org/apache/johnzon/jsonb/JohnzonBuilder.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -321,9 +321,9 @@ public byte[] fromString(final String text) {
321321
throw new IllegalArgumentException("We only support serializer on Class for now");
322322
}
323323
builder.addObjectConverter(
324-
Class.class.cast(args[0]), (ObjectConverter.Writer) (instance, jsonbGenerator) ->
324+
Class.class.cast(args[0]), (ObjectConverter.Writer) (instance, jsonbGenerator, generator) ->
325325
s.serialize(
326-
instance, jsonbGenerator.getJsonGenerator(),
326+
instance, generator,
327327
new JohnzonSerializationContext(jsonbGenerator)));
328328
});
329329
});

johnzon-jsonb/src/main/java/org/apache/johnzon/jsonb/JsonbAccessMode.java

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -337,7 +337,7 @@ private Object mapItem(final JsonValue jsonValue, final Type targetType,
337337
}
338338

339339
@Override
340-
public void writeJson(final Object instance, final MappingGenerator jsonbGenerator) {
340+
public void writeJson(final Object instance, final MappingGenerator jsonbGenerator, JsonGenerator generator) {
341341
// no-op, it's for factories only
342342
}
343343
};
@@ -1183,8 +1183,7 @@ private class WriterConverters {
11831183
final JsonbSerializer jsonbSerializer = instance.getValue();
11841184
writer = new ObjectConverter.Writer() {
11851185
@Override
1186-
public void writeJson(final Object instance, final MappingGenerator jsonbGenerator) {
1187-
final JsonGenerator generator = jsonbGenerator.getJsonGenerator();
1186+
public void writeJson(final Object instance, final MappingGenerator jsonbGenerator, JsonGenerator generator) {
11881187
jsonbSerializer.serialize(instance, generator, new JohnzonSerializationContext(jsonbGenerator));
11891188
}
11901189

johnzon-jsonb/src/test/java/org/apache/johnzon/jsonb/JohnzonConverterInJsonbTest.java

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727
import jakarta.json.JsonValue;
2828
import jakarta.json.bind.Jsonb;
2929
import jakarta.json.bind.spi.JsonbProvider;
30+
import jakarta.json.stream.JsonGenerator;
3031

3132
import org.apache.johnzon.mapper.Converter;
3233
import org.apache.johnzon.mapper.JohnzonConverter;
@@ -88,8 +89,8 @@ public static class TestDTOConverter implements ObjectConverter.Codec<TestDTO> {
8889
private static final String TIMESTAMP_JSON_KEY = "timestamp";
8990

9091
@Override
91-
public void writeJson(TestDTO instance, MappingGenerator jsonbGenerator) {
92-
jsonbGenerator.getJsonGenerator().write(TIMESTAMP_JSON_KEY, instance.instant.atZone(ZoneId.of("UTC")).toString());
92+
public void writeJson(TestDTO instance, MappingGenerator jsonbGenerator, JsonGenerator generator) {
93+
generator.write(TIMESTAMP_JSON_KEY, instance.instant.atZone(ZoneId.of("UTC")).toString());
9394
}
9495

9596
@Override

johnzon-jsonb/src/test/java/org/apache/johnzon/jsonb/SerializerTest.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,7 @@ public void passthroughSerializer() {
6969
nameHolder.name.detailName = new DetailName();
7070
nameHolder.name.detailName.name = "Another Test String";
7171
assertEquals(
72-
"{\"detailName\":{\"name\":\"Another Test String\",\"detail\":true},\"name\":{\"name\":\"Test String\"}}",
72+
"{\"name\":{\"detailName\":{\"name\":\"Another Test String\",\"detail\":true},\"name\":\"Test String\"}}",
7373
jsonb.toJson(nameHolder));
7474

7575
}

johnzon-jsonb/src/test/java/org/apache/johnzon/jsonb/SerializersRoundTripTest.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -564,7 +564,8 @@ public void roundTrip() throws Exception {
564564
original.color = Color.GREEN;*/
565565

566566
try (final Jsonb jsonb = JsonbBuilder.create()) {
567-
final Wrapper deserialized = jsonb.fromJson(jsonb.toJson(original), Wrapper.class);
567+
final String json = jsonb.toJson(original);
568+
final Wrapper deserialized = jsonb.fromJson(json, Wrapper.class);
568569
assertEquals(original, deserialized);
569570
}
570571
}
Lines changed: 129 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,129 @@
1+
/*
2+
* Licensed to the Apache Software Foundation (ASF) under one or more
3+
* contributor license agreements. See the NOTICE file distributed with
4+
* this work for additional information regarding copyright ownership.
5+
* The ASF licenses this file to You under the Apache License, Version 2.0
6+
* (the "License"); you may not use this file except in compliance with
7+
* the License. You may obtain a copy of the License at
8+
*
9+
* http://www.apache.org/licenses/LICENSE-2.0
10+
*
11+
* Unless required by applicable law or agreed to in writing, software
12+
* distributed under the License is distributed on an "AS IS" BASIS,
13+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
* See the License for the specific language governing permissions and
15+
* limitations under the License.
16+
*/
17+
package org.apache.johnzon.jsonb.serializer;
18+
19+
import java.lang.reflect.Type;
20+
21+
import org.junit.Test;
22+
23+
import jakarta.json.bind.Jsonb;
24+
import jakarta.json.bind.JsonbBuilder;
25+
import jakarta.json.bind.annotation.JsonbTypeDeserializer;
26+
import jakarta.json.bind.annotation.JsonbTypeSerializer;
27+
import jakarta.json.bind.serializer.DeserializationContext;
28+
import jakarta.json.bind.serializer.JsonbDeserializer;
29+
import jakarta.json.bind.serializer.JsonbSerializer;
30+
import jakarta.json.bind.serializer.SerializationContext;
31+
import jakarta.json.stream.JsonGenerator;
32+
import jakarta.json.stream.JsonParser;
33+
import static org.junit.Assert.assertTrue;
34+
35+
/**
36+
* This test checks a JsonbSerialize/JsonbDeserialize roundtrip when using a primitive as placeholder
37+
*/
38+
public class SerialiseAsPrimitiveAnnotatedAtClassTest {
39+
40+
41+
@JsonbTypeSerializer(ConstantJsonbSerialiser.class)
42+
@JsonbTypeDeserializer(ConstantJsonbDeserializer.class)
43+
public static class TestConstant {
44+
public final static TestConstant VAL_1 = new TestConstant("A");
45+
public final static TestConstant VAL_2 = new TestConstant("B");
46+
public final static TestConstant VAL_3 = new TestConstant("C");
47+
48+
private final String code;
49+
50+
public TestConstant(String code) {
51+
this.code = code;
52+
}
53+
54+
public String getCode() {
55+
return code;
56+
}
57+
58+
public static TestConstant getByCode(String code) {
59+
switch (code) {
60+
case "A": return VAL_1;
61+
case "B": return VAL_2;
62+
case "C": return VAL_3;
63+
default: return null;
64+
}
65+
}
66+
}
67+
68+
public static class ConstantUsage {
69+
private int i;
70+
71+
private TestConstant testConstant;
72+
73+
public int getI() {
74+
return i;
75+
}
76+
77+
public void setI(int i) {
78+
this.i = i;
79+
}
80+
81+
public TestConstant getTestConstant() {
82+
return testConstant;
83+
}
84+
85+
public void setTestConstant(TestConstant testEnum) {
86+
this.testConstant = testEnum;
87+
}
88+
}
89+
90+
public static class ConstantJsonbSerialiser implements JsonbSerializer<TestConstant> {
91+
@Override
92+
public void serialize(TestConstant val, JsonGenerator generator, SerializationContext ctx) {
93+
if (val == null) {
94+
ctx.serialize(null, generator);
95+
} else {
96+
ctx.serialize(val.getCode(), generator);
97+
}
98+
}
99+
}
100+
101+
public static class ConstantJsonbDeserializer implements JsonbDeserializer<TestConstant> {
102+
103+
@Override
104+
public TestConstant deserialize(JsonParser parser, DeserializationContext ctx, Type rtType) {
105+
if (rtType instanceof TestConstant) {
106+
final String key = parser.getString();
107+
if (key != null) {
108+
return TestConstant.getByCode(key);
109+
}
110+
}
111+
112+
return null;
113+
}
114+
}
115+
116+
117+
118+
@Test
119+
public void testEnumJsonb() {
120+
ConstantUsage enumVerwender = new ConstantUsage();
121+
enumVerwender.setI(1);
122+
enumVerwender.setTestConstant(TestConstant.VAL_2);
123+
124+
Jsonb jsonb = JsonbBuilder.create();
125+
final String json = jsonb.toJson(enumVerwender);
126+
assertTrue(json.contains("\"testConstant\":\"B\""));
127+
}
128+
129+
}

johnzon-mapper/src/main/java/org/apache/johnzon/mapper/DynamicMappingGenerator.java

Lines changed: 4 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
import java.math.BigDecimal;
2424
import java.math.BigInteger;
2525

26+
@Deprecated
2627
public class DynamicMappingGenerator implements MappingGenerator {
2728
private final MappingGenerator delegate;
2829
private final Runnable writeStart;
@@ -40,33 +41,16 @@ public DynamicMappingGenerator(final MappingGenerator delegate,
4041
this.writeEnd = writeEnd;
4142
this.keyName = keyName;
4243
}
43-
44-
protected JsonGenerator getRawJsonGenerator() {
45-
return delegate.getJsonGenerator();
46-
}
47-
48-
@Override
49-
public JsonGenerator getJsonGenerator() {
50-
return generator == null ? generator = new InObjectOrPrimitiveJsonGenerator(
51-
getRawJsonGenerator(), writeStart, keyName, writeEnd) : generator;
52-
}
44+
5345

5446
@Override
5547
public MappingGenerator writeObject(final String key, final Object o, final JsonGenerator generator) {
56-
return delegate.writeObject(key, o, ensureGenerator(generator));
48+
return delegate.writeObject(key, o, generator);
5749
}
5850

5951
@Override
6052
public MappingGenerator writeObject(final Object o, final JsonGenerator generator) {
61-
return delegate.writeObject(o, ensureGenerator(generator));
62-
}
63-
64-
private JsonGenerator ensureGenerator(final JsonGenerator generator) {
65-
if (this.generator != null && this.generator != generator && this.generator.delegate != generator) {
66-
this.generator = null;
67-
reset();
68-
}
69-
return getJsonGenerator(); // ensure we wrap it
53+
return delegate.writeObject(o, generator);
7054
}
7155

7256
protected void reset() {
@@ -593,24 +577,5 @@ public SkipEnclosingWriteEnd(final MappingGenerator delegate, final String keyNa
593577
super(delegate, NOOP, NOOP, keyName);
594578
this.rawGenerator = generator;
595579
}
596-
597-
@Override
598-
protected JsonGenerator getRawJsonGenerator() {
599-
return rawGenerator;
600-
}
601-
602-
@Override
603-
public JsonGenerator getJsonGenerator() {
604-
if (skippingGenerator == null) {
605-
skippingGenerator = new SkipLastWriteEndGenerator(super.getJsonGenerator());
606-
}
607-
return skippingGenerator;
608-
}
609-
610-
@Override
611-
protected void reset() {
612-
super.reset();
613-
skippingGenerator = null;
614-
}
615580
}
616581
}

johnzon-mapper/src/main/java/org/apache/johnzon/mapper/Mapper.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -203,7 +203,7 @@ public void writeObject(final Object object, final OutputStream stream) {
203203

204204
private void writeObject(final Object object, final JsonGenerator generator, final Collection<String> ignored,
205205
final JsonPointerTracker tracker) {
206-
final MappingGeneratorImpl mappingGenerator = new MappingGeneratorImpl(config, generator, mappings);
206+
final MappingGeneratorImpl mappingGenerator = new MappingGeneratorImpl(config, mappings);
207207
mappingGenerator.doWriteObject(object, generator, true, ignored, tracker);
208208
}
209209

johnzon-mapper/src/main/java/org/apache/johnzon/mapper/MapperConfig.java

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,8 @@
2525

2626
import jakarta.json.Json;
2727
import jakarta.json.JsonValue;
28+
import jakarta.json.stream.JsonGenerator;
29+
2830
import java.lang.reflect.Type;
2931
import java.nio.charset.Charset;
3032
import java.util.Collection;
@@ -48,7 +50,7 @@
4850

4951
private static final ObjectConverter.Codec NO_CONVERTER = new ObjectConverter.Codec() {
5052
@Override
51-
public void writeJson(Object instance, MappingGenerator jsonbGenerator) {
53+
public void writeJson(Object instance, MappingGenerator jsonbGenerator, JsonGenerator generator) {
5254
// just a dummy
5355
}
5456

johnzon-mapper/src/main/java/org/apache/johnzon/mapper/MappingGenerator.java

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -24,16 +24,11 @@
2424
* Handles writing Json for Objects.
2525
* Internally it uses a {@link JsonGenerator} to write JSON
2626
*
27-
* To write JSON-P structure elements you can use the {@link #getJsonGenerator()} method.
27+
* To write JSON-P structure elements you can use the {@link JsonGenerator}.
2828
*
2929
*/
3030
public interface MappingGenerator {
3131

32-
/**
33-
* @return the {@link JsonGenerator} used internally to write the JSON output.
34-
*/
35-
JsonGenerator getJsonGenerator();
36-
3732
/**
3833
* Write the given Object o into the current JSON layer.
3934
* This will <em>not</em> open a new json layer ('{', '}')

0 commit comments

Comments
 (0)