Skip to content

Commit 4363e2b

Browse files
committed
feat(70552): refactoring to Jackson from DSLJson
1 parent 27bf34a commit 4363e2b

File tree

9 files changed

+140
-97
lines changed

9 files changed

+140
-97
lines changed

pom.xml

Lines changed: 19 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
<testcontainers.version>1.18.3</testcontainers.version>
1414
<spotlessMavenPlugin.version>2.43.0</spotlessMavenPlugin.version>
1515
<googleJavaFormat.version>1.23.0</googleJavaFormat.version>
16+
<jackson.version>2.20.1</jackson.version>
1617
</properties>
1718

1819
<profiles>
@@ -53,9 +54,24 @@
5354
<version>3.21.12</version>
5455
</dependency>
5556
<dependency>
56-
<groupId>com.dslplatform</groupId>
57-
<artifactId>dsl-json</artifactId>
58-
<version>2.0.2</version>
57+
<groupId>com.fasterxml.jackson.core</groupId>
58+
<artifactId>jackson-databind</artifactId>
59+
<version>${jackson.version}</version>
60+
</dependency>
61+
<dependency>
62+
<groupId>com.fasterxml.jackson.core</groupId>
63+
<artifactId>jackson-core</artifactId>
64+
<version>${jackson.version}</version>
65+
</dependency>
66+
<dependency>
67+
<groupId>com.fasterxml.jackson.core</groupId>
68+
<artifactId>jackson-annotations</artifactId>
69+
<version>2.20</version>
70+
</dependency>
71+
<dependency>
72+
<groupId>com.fasterxml.jackson.datatype</groupId>
73+
<artifactId>jackson-datatype-jsr310</artifactId>
74+
<version>${jackson.version}</version>
5975
</dependency>
6076
<dependency>
6177
<groupId>org.apache.pulsar</groupId>
Lines changed: 55 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,33 @@
11
package fi.hsl.common.hfp;
22

3-
import com.dslplatform.json.*;
3+
import com.fasterxml.jackson.annotation.JsonAlias;
4+
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
5+
import com.fasterxml.jackson.annotation.JsonProperty;
6+
import com.fasterxml.jackson.core.JsonGenerator;
7+
import com.fasterxml.jackson.core.JsonParser;
8+
import com.fasterxml.jackson.core.JsonProcessingException;
9+
import com.fasterxml.jackson.databind.DeserializationContext;
10+
import com.fasterxml.jackson.databind.JsonDeserializer;
11+
import com.fasterxml.jackson.databind.JsonSerializer;
12+
import com.fasterxml.jackson.databind.SerializerProvider;
13+
import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
14+
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
415

516
import java.io.IOException;
617

7-
// ignore unknown properties (default for objects).
8-
// to disallow unknown properties in JSON set it to FAIL which will result in exception instead
9-
@CompiledJson(onUnknown = CompiledJson.Behavior.IGNORE)
18+
@JsonIgnoreProperties(ignoreUnknown = true)
1019
public class HfpJson {
1120
//Specification: https://digitransit.fi/en/developers/apis/4-realtime-api/vehicle-positions/
1221
//Example payload:
1322
// {"VP":{"desi":"81","dir":"2","oper":22,"veh":792,"tst":"2018-04-05T17:38:36Z","tsi":1522949916,"spd":0.16,"hdg":225,"lat":60.194481,"long":25.03095,"acc":0,"dl":-25,"odo":2819,"drst":0,"oday":"2018-04-05","jrn":636,"line":112,"start":"20:25"}}
1423

15-
@JsonAttribute(nullable = false, name = "VP", alternativeNames = {"DUE", "ARR", "DEP", "ARS", "PDE", "PAS", "WAIT",
16-
"DOO", "DOC", "TLR", "TLA", "DA", "DOUT", "BA", "BOUT", "VJA", "VJOUT"})
24+
@JsonProperty("VP")
25+
@JsonAlias({"DUE", "ARR", "DEP", "ARS", "PDE", "PAS", "WAIT",
26+
"DOO", "DOC", "TLR", "TLA", "DA", "DOUT", "BA", "BOUT",
27+
"VJA", "VJOUT"})
1728
public Payload payload;
1829

19-
@CompiledJson(onUnknown = CompiledJson.Behavior.IGNORE)
30+
@JsonIgnoreProperties(ignoreUnknown = true)
2031
public static class Payload {
2132

2233
public String desi;
@@ -27,10 +38,10 @@ public static class Payload {
2738

2839
public Integer veh;
2940

30-
@JsonAttribute(nullable = false)
41+
@JsonProperty(required = true)
3142
public String tst;
3243

33-
@JsonAttribute(nullable = false)
44+
@JsonProperty(required = true)
3445
public long tsi;
3546

3647
public Double spd;
@@ -39,14 +50,15 @@ public static class Payload {
3950

4051
public Double lat;
4152

42-
@JsonAttribute(name = "long") //use alternative name in JSON
53+
@JsonProperty("long") //use alternative name in JSON
4354
public Double longitude;
4455

4556
public Double acc;
4657

4758
public Integer dl;
4859

49-
@JsonAttribute(converter = Odo.class)
60+
@JsonDeserialize(using = OdoDeserializer.class)
61+
@JsonSerialize(using = OdoSerializer.class)
5062
public Double odo;
5163

5264
public Integer drst;
@@ -73,65 +85,70 @@ public static class Payload {
7385

7486
public String ttdep;
7587

76-
@JsonAttribute(name = "dr-type") //use alternative name in JSON
88+
@JsonProperty("dr-type") //use alternative name in JSON
7789
public Integer dr_type;
7890

79-
@JsonAttribute(name = "tlp-requestid") //use alternative name in JSON
91+
@JsonProperty("tlp-requestid")
8092
public Integer tlp_requestid;
8193

82-
@JsonAttribute(name = "tlp-requesttype") //use alternative name in JSON
94+
@JsonProperty("tlp-requesttype")
8395
public String tlp_requesttype;
8496

85-
@JsonAttribute(name = "tlp-prioritylevel") //use alternative name in JSON
97+
@JsonProperty("tlp-prioritylevel")
8698
public String tlp_prioritylevel;
8799

88-
@JsonAttribute(name = "tlp-reason") //use alternative name in JSON
100+
@JsonProperty("tlp-reason")
89101
public String tlp_reason;
90102

91-
@JsonAttribute(name = "tlp-att-seq") //use alternative name in JSON
103+
@JsonProperty("tlp-att-seq")
92104
public Integer tlp_att_seq;
93105

94-
@JsonAttribute(name = "tlp-decision") //use alternative name in JSON
106+
@JsonProperty("tlp-decision")
95107
public String tlp_decision;
96108

97109
public Integer sid;
98110

99-
@JsonAttribute(name = "signal-groupid") //use alternative name in JSON
111+
@JsonProperty("signal-groupid")
100112
public Integer signal_groupid;
101113

102-
@JsonAttribute(name = "tlp-signalgroupnbr") //use alternative name in JSON
114+
@JsonProperty("tlp-signalgroupnbr")
103115
public Integer tlp_signalgroupnbr;
104116

105-
@JsonAttribute(name = "tlp-line-configid") //use alternative name in JSON
117+
@JsonProperty("tlp-line-configid")
106118
public Integer tlp_line_configid;
107119

108-
@JsonAttribute(name = "tlp-point-configid") //use alternative name in JSON
120+
@JsonProperty("tlp-point-configid")
109121
public Integer tlp_point_configid;
110122

111-
@JsonAttribute(name = "tlp-frequency") //use alternative name in JSON
123+
@JsonProperty("tlp-frequency")
112124
public Integer tlp_frequency;
113125

114-
@JsonAttribute(name = "tlp-protocol") //use alternative name in JSON
126+
@JsonProperty("tlp-protocol")
115127
public String tlp_protocol;
116128

117129
public String label;
118130
}
119131

120-
public static abstract class Odo {
121-
public static final JsonReader.ReadObject<Double> JSON_READER = new JsonReader.ReadObject<Double>() {
122-
public Double read(JsonReader reader) throws IOException {
123-
return reader.wasNull() ? null : NumberConverter.deserializeDouble(reader);
132+
public static class OdoDeserializer extends JsonDeserializer<Double> {
133+
@Override
134+
public Double deserialize(JsonParser p, DeserializationContext ctxt)
135+
throws IOException, JsonProcessingException {
136+
if (p.currentToken().isNumeric()) {
137+
return p.getDoubleValue();
124138
}
125-
};
126-
127-
public static final JsonWriter.WriteObject<Double> JSON_WRITER = new JsonWriter.WriteObject<Double>() {
128-
public void write(JsonWriter writer, Double value) {
129-
if (value == null)
130-
writer.writeNull();
131-
else
132-
NumberConverter.serializeNullable(value.intValue(), writer);
133-
}
134-
};
139+
return null;
140+
}
135141
}
136142

143+
public static class OdoSerializer extends JsonSerializer<Double> {
144+
@Override
145+
public void serialize(Double value, JsonGenerator gen, SerializerProvider serializers)
146+
throws IOException {
147+
if (value == null) {
148+
gen.writeNull();
149+
} else {
150+
gen.writeNumber(value.intValue());
151+
}
152+
}
153+
}
137154
}

src/main/java/fi/hsl/common/hfp/HfpParser.java

Lines changed: 31 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,15 @@
11
package fi.hsl.common.hfp;
22

3-
import com.dslplatform.json.DslJson;
4-
import com.dslplatform.json.ParsingException;
3+
import com.fasterxml.jackson.core.JsonProcessingException;
4+
import com.fasterxml.jackson.databind.DeserializationFeature;
5+
import com.fasterxml.jackson.databind.ObjectMapper;
6+
import com.fasterxml.jackson.databind.SerializationFeature;
57
import fi.hsl.common.hfp.proto.Hfp;
68
import org.jetbrains.annotations.NotNull;
79
import org.jetbrains.annotations.Nullable;
810
import org.slf4j.Logger;
911
import org.slf4j.LoggerFactory;
1012

11-
import javax.validation.constraints.Null;
1213
import java.io.IOException;
1314
import java.sql.Date;
1415
import java.sql.Time;
@@ -28,13 +29,11 @@ public class HfpParser {
2829

2930
private static Set<String> vehiclesWithEmptyTransportMode = new HashSet<>();
3031

31-
// Let's use dsl-json (https://github.com/ngs-doo/dsl-json) for performance.
32-
// Based on this benchmark: https://github.com/fabienrenaud/java-json-benchmark
33-
34-
//Example: https://github.com/ngs-doo/dsl-json/blob/master/examples/MavenJava8/src/main/java/com/dslplatform/maven/Example.java
35-
36-
//Note! Apparently not thread safe, for per thread reuse use ThreadLocal pattern or create separate instances
37-
final DslJson<Object> dslJson = new DslJson<>(new DslJson.Settings<>().allowArrayFormat(true).includeServiceLoader());
32+
final ObjectMapper objectMapper = new ObjectMapper()
33+
.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false)
34+
.configure(DeserializationFeature.ACCEPT_SINGLE_VALUE_AS_ARRAY, true)
35+
.configure(DeserializationFeature.ACCEPT_EMPTY_STRING_AS_NULL_OBJECT, true)
36+
.configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false);
3837

3938
private static void foundVehicleWithEmptyTransportMode(String uniqueVehicleId) {
4039
vehiclesWithEmptyTransportMode.add(uniqueVehicleId);
@@ -53,32 +52,35 @@ public static HfpParser newInstance() {
5352
/**
5453
* Methods for parsing the Json Payload
5554
**/
56-
5755
@Nullable
5856
public HfpJson parseJson(@NotNull byte[] data) throws IOException, InvalidHfpPayloadException {
5957
try {
60-
return dslJson.deserialize(HfpJson.class, data, data.length);
58+
HfpJson hfpJson = objectMapper.readValue(data, HfpJson.class);
59+
validateRequiredFields(hfpJson);
60+
return hfpJson;
6161
} catch (IOException ioe) {
62-
if (ioe instanceof ParsingException) {
63-
throw new InvalidHfpPayloadException("Failed to parse HFP JSON", (ParsingException) ioe);
64-
} else {
65-
throw ioe;
66-
}
62+
throw new InvalidHfpPayloadException("Failed to parse HFP JSON", ioe);
63+
}
64+
}
65+
66+
private void validateRequiredFields(HfpJson hfpJson) throws InvalidHfpPayloadException {
67+
if (hfpJson == null || hfpJson.payload == null) {
68+
throw new InvalidHfpPayloadException("Payload is missing or null");
69+
}
70+
HfpJson.Payload p = hfpJson.payload;
71+
if (p.tst == null) {
72+
throw new InvalidHfpPayloadException("Field 'tst' cannot be null");
6773
}
6874
}
6975

7076
@NotNull
7177
public String serializeToString(@NotNull final HfpJson json) throws IOException {
72-
final ByteArrayOutputStream os = new ByteArrayOutputStream();
73-
dslJson.serialize(json, os);
74-
return os.toString("UTF-8");
78+
return objectMapper.writeValueAsString(json);
7579
}
7680

7781
@NotNull
7882
public byte[] serializeToByteArray(@NotNull final HfpJson json) throws IOException {
79-
final ByteArrayOutputStream os = new ByteArrayOutputStream();
80-
dslJson.serialize(json, os);
81-
return os.toByteArray();
83+
return objectMapper.writeValueAsBytes(json);
8284
}
8385

8486
public Optional<Hfp.Payload> safeParse(@NotNull byte[] data) {
@@ -96,12 +98,11 @@ public static Hfp.Payload parsePayload(@NotNull HfpJson json) {
9698
final HfpJson.Payload payload = json.payload;
9799

98100
Hfp.Payload.Builder builder = Hfp.Payload.newBuilder();
99-
// Required attributes
101+
100102
builder.setSchemaVersion(builder.getSchemaVersion());
101103
HfpValidator.validateString(payload.tst).ifPresent(builder::setTst); // TODO add validation for offsetdatetime format
102104
builder.setTsi(payload.tsi);
103105

104-
// Optional attributes
105106
HfpValidator.validateString(payload.desi).ifPresent(builder::setDesi);
106107
HfpValidator.validateString(payload.dir).ifPresent(builder::setDir);
107108
if (payload.oper != null)
@@ -173,7 +174,6 @@ public static Hfp.Payload parsePayload(@NotNull HfpJson json) {
173174
/**
174175
* Methods for parsing the data from the topic
175176
*/
176-
177177
public static Optional<Hfp.Topic> safeParseTopic(@NotNull String topic) {
178178
try {
179179
return Optional.of(parseTopic(topic));
@@ -440,7 +440,11 @@ private InvalidHfpTopicException(String message) {
440440
}
441441

442442
public static class InvalidHfpPayloadException extends Exception {
443-
private InvalidHfpPayloadException(String message, ParsingException cause) {
443+
public InvalidHfpPayloadException(String message) {
444+
super(message);
445+
}
446+
447+
public InvalidHfpPayloadException(String message, Throwable cause) {
444448
super(message, cause);
445449
}
446450
}

src/main/java/fi/hsl/common/passengercount/PassengerCountParser.java

Lines changed: 16 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
11
package fi.hsl.common.passengercount;
22

3-
import com.dslplatform.json.DslJson;
4-
import com.dslplatform.json.ParsingException;
3+
import com.fasterxml.jackson.core.JsonProcessingException;
4+
import com.fasterxml.jackson.databind.DeserializationFeature;
5+
import com.fasterxml.jackson.databind.ObjectMapper;
6+
import com.fasterxml.jackson.databind.SerializationFeature;
57
import fi.hsl.common.passengercount.json.*;
68
import fi.hsl.common.passengercount.proto.PassengerCount;
79
import org.jetbrains.annotations.NotNull;
@@ -19,10 +21,11 @@ public class PassengerCountParser {
1921

2022
static final Pattern topicVersionRegex = Pattern.compile("(^v\\d+|dev)");
2123

22-
DslJson<Object> dslJson = new DslJson<>(
23-
new DslJson.Settings<>()
24-
.allowArrayFormat(true)
25-
.includeServiceLoader());
24+
ObjectMapper objectMapper = new ObjectMapper()
25+
.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false)
26+
.configure(DeserializationFeature.ACCEPT_SINGLE_VALUE_AS_ARRAY, true)
27+
.configure(DeserializationFeature.ACCEPT_EMPTY_STRING_AS_NULL_OBJECT, true)
28+
.configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false);
2629

2730
@NotNull
2831
public static PassengerCountParser newInstance() {
@@ -261,23 +264,23 @@ public ApcJson toJson(PassengerCount.Payload passengerCountPayload) {
261264
}
262265

263266
public OutputStream serializeJson(ApcJson apcJson, OutputStream outputStream) throws IOException {
264-
dslJson.serialize(apcJson, outputStream);
267+
objectMapper.writeValue(outputStream, apcJson);
265268
return outputStream;
266269
}
267270

268271
@Nullable
269272
public ApcJson parseJson(byte @NotNull [] data) throws IOException, InvalidAPCPayloadException {
270273
try {
271-
return dslJson.deserialize(ApcJson.class, data, data.length);
274+
return objectMapper.readValue(data, ApcJson.class);
272275
} catch (IOException ioe) {
273-
if (ioe instanceof ParsingException) {
274-
throw new PassengerCountParser.InvalidAPCPayloadException("Failed to parse APC JSON",
275-
(ParsingException) ioe);
276+
if (ioe instanceof com.fasterxml.jackson.core.JsonProcessingException jpe) {
277+
throw new PassengerCountParser.InvalidAPCPayloadException(
278+
"Failed to parse APC JSON", jpe
279+
);
276280
} else {
277281
throw ioe;
278282
}
279283
}
280-
281284
}
282285

283286
private static OptionalDouble safeParseDouble(String s) {
@@ -336,7 +339,7 @@ private InvalidAPCTopicException(String message) {
336339
}
337340

338341
public static class InvalidAPCPayloadException extends Exception {
339-
private InvalidAPCPayloadException(String message, ParsingException cause) {
342+
private InvalidAPCPayloadException(String message, JsonProcessingException cause) {
340343
super(message, cause);
341344
}
342345
}

0 commit comments

Comments
 (0)