Skip to content

Commit

Permalink
Serialize long as string and add tests
Browse files Browse the repository at this point in the history
  • Loading branch information
jhaber committed Jul 11, 2023
1 parent b77f31b commit 1e66883
Show file tree
Hide file tree
Showing 3 changed files with 106 additions and 3 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -8,15 +8,18 @@ public class ProtobufJacksonConfig {
private final ExtensionRegistryWrapper extensionRegistry;
private final boolean acceptLiteralFieldnames;
private final boolean properUnsignedNumberSerialization;
private final boolean serializeLongsAsString;

private ProtobufJacksonConfig(
ExtensionRegistryWrapper extensionRegistry,
boolean acceptLiteralFieldnames,
boolean properUnsignedNumberSerialization
boolean properUnsignedNumberSerialization,
boolean serializeLongsAsString
) {
this.extensionRegistry = extensionRegistry;
this.acceptLiteralFieldnames = acceptLiteralFieldnames;
this.properUnsignedNumberSerialization = properUnsignedNumberSerialization;
this.serializeLongsAsString = serializeLongsAsString;
}

public static ProtobufJacksonConfig getDefaultInstance() {
Expand All @@ -39,10 +42,15 @@ public boolean properUnsignedNumberSerialization() {
return properUnsignedNumberSerialization;
}

public boolean serializeLongsAsString() {
return serializeLongsAsString;
}

public static class Builder {
private ExtensionRegistryWrapper extensionRegistry = ExtensionRegistryWrapper.empty();
private boolean acceptLiteralFieldnames = false;
private boolean properUnsignedNumberSerialization = false;
private boolean serializeLongsAsString = false;

private Builder() {}

Expand All @@ -60,16 +68,28 @@ public Builder acceptLiteralFieldnames(boolean acceptLiteralFieldnames) {
return this;
}

public Builder useCanonicalSerialization() {
properUnsignedNumberSerialization(true);
serializeLongsAsString(true);
return this;
}

public Builder properUnsignedNumberSerialization(boolean properUnsignedNumberSerialization) {
this.properUnsignedNumberSerialization = properUnsignedNumberSerialization;
return this;
}

public Builder serializeLongsAsString(boolean serializeLongsAsString) {
this.serializeLongsAsString = serializeLongsAsString;
return this;
}

public ProtobufJacksonConfig build() {
return new ProtobufJacksonConfig(
extensionRegistry,
acceptLiteralFieldnames,
properUnsignedNumberSerialization
properUnsignedNumberSerialization,
serializeLongsAsString
);
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -137,7 +137,13 @@ private void writeLong(FieldDescriptor field, long value, JsonGenerator generato
isUnsigned(field.getType())
) {
BigInteger unsignedValue = BigInteger.valueOf(value & Long.MAX_VALUE).setBit(Long.SIZE - 1);
generator.writeNumber(unsignedValue);
if (config.serializeLongsAsString()) {
generator.writeString(unsignedValue.toString());
} else {
generator.writeNumber(unsignedValue);
}
} else if (config.serializeLongsAsString()) {
generator.writeString(Long.toString(value));
} else {
generator.writeNumber(value);
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
package com.hubspot.jackson.datatype.protobuf;

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

import com.fasterxml.jackson.databind.ObjectMapper;
import com.google.protobuf.util.JsonFormat;
import com.hubspot.jackson.datatype.protobuf.util.ProtobufCreator;
import com.hubspot.jackson.datatype.protobuf.util.TestProtobuf.AllFields;
import com.hubspot.jackson.datatype.protobuf.util.TestProtobuf3.AllFieldsProto3;
import java.io.IOException;
import org.junit.Test;

public class JsonFormatCompatibilityTest {
private static final ObjectMapper MAPPER = new ObjectMapper().registerModule(
new ProtobufModule(ProtobufJacksonConfig.builder().useCanonicalSerialization().build())
);

@Test
public void weSerializeAndJsonFormatDeserializesProto2() throws IOException {
repeat(() -> {
AllFields original = ProtobufCreator.create(AllFields.class);
String json = MAPPER.writeValueAsString(original);

AllFields.Builder builder = AllFields.newBuilder();
JsonFormat.parser().merge(json, builder);

assertThat(builder.build()).isEqualTo(original);
}, 1_000);
}

@Test
public void jsonFormatSerializesAndWeDeserializeProto2() throws IOException {
repeat(() -> {
AllFields original = ProtobufCreator.create(AllFields.class);
String json = JsonFormat.printer().print(original);

AllFields parsed = MAPPER.readValue(json, AllFields.class);

assertThat(parsed).isEqualTo(original);
}, 1_000);
}

@Test
public void weSerializeAndJsonFormatDeserializesProto3() throws IOException {
repeat(() -> {
AllFieldsProto3 original = ProtobufCreator.create(AllFieldsProto3.class);
String json = MAPPER.writeValueAsString(original);

AllFieldsProto3.Builder builder = AllFieldsProto3.newBuilder();
JsonFormat.parser().merge(json, builder);

assertThat(builder.build()).isEqualTo(original);
}, 1_000);
}

@Test
public void jsonFormatSerializesAndWeDeserializeProto3() throws IOException {
repeat(() -> {
AllFieldsProto3 original = ProtobufCreator.create(AllFieldsProto3.class);
String json = JsonFormat.printer().print(original);

AllFieldsProto3 parsed = MAPPER.readValue(json, AllFieldsProto3.class);

assertThat(parsed).isEqualTo(original);
}, 1_000);
}

private interface Runnable {
void run() throws IOException;
}

private static void repeat(Runnable r, int times) throws IOException {
for (int i = 0; i < times; i++) {
r.run();
}
}
}

0 comments on commit 1e66883

Please sign in to comment.