public class DurationDeserializer extends StdDeserializer<Duration> {

  public DurationDeserializer() {
    super(Duration.class);
  }

  @Override
  public Duration deserialize(JsonParser parser, DeserializationContext context) throws IOException {
    switch (parser.getCurrentToken()) {
      case VALUE_STRING:
        try {
          return Durations.parse(parser.getText());
        } catch (ParseException e) {
          throw context.weirdStringException(parser.getText(), Duration.class, e.getMessage());
        }
      default:
        context.reportWrongTokenException(Duration.class, JsonToken.VALUE_STRING, wrongTokenMessage(context));
        // the previous method should have thrown
        throw new AssertionError();
    }
  }

  // TODO share this?
  private static String wrongTokenMessage(DeserializationContext context) {
    return "Can not deserialize instance of com.google.protobuf.Duration out of " + context.getParser().currentToken() + " token";
  }
} public class FieldMaskDeserializer extends StdDeserializer<FieldMask> {

  public FieldMaskDeserializer() {
    super(FieldMask.class);
  }

  @Override
  public FieldMask deserialize(JsonParser parser, DeserializationContext context) throws IOException {
    switch (parser.getCurrentToken()) {
      case VALUE_STRING:
        return FieldMaskUtil.fromJsonString(parser.getText());
      default:
        context.reportWrongTokenException(FieldMask.class, JsonToken.VALUE_STRING, wrongTokenMessage(context));
        // the previous method should have thrown
        throw new AssertionError();
    }
  }

  // TODO share this?
  private static String wrongTokenMessage(DeserializationContext context) {
    return "Can not deserialize instance of com.google.protobuf.FieldMask out of " + context.getParser().currentToken() + " token";
  }
} public class ListValueDeserializer extends ProtobufDeserializer<ListValue, ListValue.Builder> {
  private static final FieldDescriptor VALUES_FIELD = ListValue.getDescriptor().findFieldByName("values");

  public ListValueDeserializer() {
    super(ListValue.class);
  }

  @Override
  protected void populate(
          ListValue.Builder builder,
          JsonParser parser,
          DeserializationContext context
  ) throws IOException {
    List<Object> values = readArray(builder, VALUES_FIELD, null, parser, context);
    for (Object value : values) {
      builder.addRepeatedField(VALUES_FIELD, value);
    }
  }
} public class NullValueDeserializer extends StdDeserializer<NullValue> {

  public NullValueDeserializer() {
    super(NullValue.class);
  }

  @Override
  public NullValue deserialize(JsonParser parser, DeserializationContext context) throws IOException {
    switch (parser.getCurrentToken()) {
      case VALUE_NULL:
        return NullValue.NULL_VALUE;
      default:
        context.reportWrongTokenException(NullValue.class, JsonToken.VALUE_NULL, wrongTokenMessage(context));
        // the previous method should have thrown
        throw new AssertionError();
    }
  }

  @Override
  public NullValue getNullValue(DeserializationContext ctxt) {
    return NullValue.NULL_VALUE;
  }

  // TODO share this?
  private static String wrongTokenMessage(DeserializationContext context) {
    return "Can not deserialize instance of com.google.protobuf.NullValue out of " + context.getParser().currentToken() + " token";
  }
} public class StructDeserializer extends ProtobufDeserializer<Struct, Struct.Builder> {
  private static final FieldDescriptor FIELDS_FIELD = Struct.getDescriptor().findFieldByName("fields");

  public StructDeserializer() {
    super(Struct.class);
  }

  @Override
  protected void populate(
          Struct.Builder builder,
          JsonParser parser,
          DeserializationContext context
  ) throws IOException {
    List<Message> entries = readMap(builder, FIELDS_FIELD, parser, context);
    for (Message entry : entries) {
      builder.addRepeatedField(FIELDS_FIELD, entry);
    }
  }
} public class TimestampDeserializer extends StdDeserializer<Timestamp> {

  public TimestampDeserializer() {
    super(Timestamp.class);
  }

  @Override
  public Timestamp deserialize(JsonParser parser, DeserializationContext context) throws IOException {
    switch (parser.getCurrentToken()) {
      case VALUE_STRING:
        try {
          return Timestamps.parse(parser.getText());
        } catch (ParseException e) {
          throw context.weirdStringException(parser.getText(), Timestamp.class, e.getMessage());
        }
      default:
        context.reportWrongTokenException(Timestamp.class, JsonToken.VALUE_STRING, wrongTokenMessage(context));
        // the previous method should have thrown
        throw new AssertionError();
    }
  }

  // TODO share this?
  private static String wrongTokenMessage(DeserializationContext context) {
    return "Can not deserialize instance of com.google.protobuf.Timestamp out of " + context.getParser().currentToken() + " token";
  }
} public class ValueDeserializer extends ProtobufDeserializer<Value, Value.Builder> {
  private static final FieldDescriptor STRUCT_FIELD = Value.getDescriptor().findFieldByName("struct_value");
  private static final FieldDescriptor LIST_FIELD = Value.getDescriptor().findFieldByName("list_value");

  public ValueDeserializer() {
    super(Value.class);
  }

  @Override
  protected void populate(
          Value.Builder builder,
          JsonParser parser,
          DeserializationContext context
  ) throws IOException {
    switch (parser.getCurrentToken()) {
      case START_OBJECT:
        Object structValue = readValue(builder, STRUCT_FIELD, null, parser, context);
        builder.setField(STRUCT_FIELD, structValue);
        return;
      case START_ARRAY:
        Object listValue = readValue(builder, LIST_FIELD, null, parser, context);
        builder.setField(LIST_FIELD, listValue);
        return;
      case VALUE_STRING:
        builder.setStringValue(parser.getText());
        return;
      case VALUE_NUMBER_INT:
      case VALUE_NUMBER_FLOAT:
        builder.setNumberValue(parser.getValueAsDouble());
        return;
      case VALUE_TRUE:
        builder.setBoolValue(true);
        return;
      case VALUE_FALSE:
        builder.setBoolValue(false);
        return;
      case VALUE_NULL:
        builder.setNullValue(NullValue.NULL_VALUE);
        return;
      default:
        String message = "Can not deserialize instance of com.google.protobuf.Value out of " + parser.currentToken() + " token";
        context.reportInputMismatch(Value.class, message);
        // the previous method should have thrown
        throw new AssertionError();
    }
  }

  @Override
  public Value.Builder getNullValue(DeserializationContext ctxt) {
    return Value.newBuilder().setNullValue(NullValue.NULL_VALUE);
  }
} public class WrappedPrimitiveDeserializer<T extends Message, V extends Builder> extends ProtobufDeserializer<T, V> {

  public WrappedPrimitiveDeserializer(Class<T> wrapperType) {
    super(wrapperType);
  }

  @Override
  protected void populate(V builder, JsonParser parser, DeserializationContext context) throws IOException {
    FieldDescriptor field = builder.getDescriptorForType().findFieldByName("value");
    Object value = readValue(builder, field, null, parser, context);
    builder.setField(field, value);
  }
} public class DurationSerializer extends ProtobufSerializer<Duration> {

  public DurationSerializer() {
    super(Duration.class);
  }

  @Override
  public void serialize(
          Duration duration,
          JsonGenerator generator,
          SerializerProvider serializerProvider
  ) throws IOException {
    generator.writeString(Durations.toString(duration));
  }
} public class FieldMaskSerializer extends ProtobufSerializer<FieldMask> {

  public FieldMaskSerializer() {
    super(FieldMask.class);
  }

  @Override
  public void serialize(
          FieldMask fieldMask,
          JsonGenerator generator,
          SerializerProvider serializerProvider
  ) throws IOException {
    generator.writeString(FieldMaskUtil.toJsonString(fieldMask));
  }
} public class ListValueSerializer extends ProtobufSerializer<ListValue> {
  private static final FieldDescriptor VALUES_FIELD = ListValue.getDescriptor().findFieldByName("values");

  public ListValueSerializer() {
    super(ListValue.class);
  }

  @Override
  public void serialize(
          ListValue listValue,
          JsonGenerator generator,
          SerializerProvider serializerProvider
  ) throws IOException {
    generator.writeStartArray();
    for (Value value : listValue.getValuesList()) {
      writeValue(VALUES_FIELD, value, generator, serializerProvider);
    }
    generator.writeEndArray();
  }
} public class MessageSerializer extends ProtobufSerializer<MessageOrBuilder> {
  @SuppressFBWarnings(value="SE_BAD_FIELD")
  private final ProtobufJacksonConfig config;
  private final Map<Descriptor, PropertyNamingCache> propertyNamingCache;

  /**
   * @deprecated use {@link #MessageSerializer(ProtobufJacksonConfig)} instead
   */
  @Deprecated
  public MessageSerializer(ExtensionRegistryWrapper extensionRegistry) {
    this(ProtobufJacksonConfig.builder().extensionRegistry(extensionRegistry).build());
  }

  public MessageSerializer(ProtobufJacksonConfig config) {
    super(MessageOrBuilder.class);

    this.config = config;
    this.propertyNamingCache = new ConcurrentHashMap<>();
  }

  @Override
  public void serialize(
          MessageOrBuilder message,
          JsonGenerator generator,
          SerializerProvider serializerProvider
  ) throws IOException {
    generator.writeStartObject();

    boolean proto3 = message.getDescriptorForType().getFile().getSyntax() == Syntax.PROTO3;
    Include include = serializerProvider.getConfig().getDefaultPropertyInclusion().getValueInclusion();
    boolean writeDefaultValues = proto3 && include != Include.NON_DEFAULT;
    boolean writeEmptyCollections = include != Include.NON_DEFAULT && include != Include.NON_EMPTY;

    Descriptor descriptor = message.getDescriptorForType();
    Function<FieldDescriptor, String> propertyNaming = getPropertyNaming(descriptor, serializerProvider);
    List<FieldDescriptor> fields = descriptor.getFields();
    if (message instanceof ExtendableMessageOrBuilder<?>) {
      fields = new ArrayList<>(fields);

      for (ExtensionInfo extensionInfo : config.extensionRegistry().getExtensionsByDescriptor(descriptor)) {
        fields.add(extensionInfo.descriptor);
      }
    }

    for (FieldDescriptor field : fields) {
      String fieldName = propertyNaming.apply(field);

      if (field.isRepeated()) {
        List<?> valueList = (List<?>) message.getField(field);

        if (!valueList.isEmpty() || writeEmptyCollections) {
          if (field.isMapField()) {
            generator.writeFieldName(fieldName);
            writeMap(field, valueList, generator, serializerProvider);
          } else if (valueList.size() == 1 && writeSingleElementArraysUnwrapped(serializerProvider)) {
            generator.writeFieldName(fieldName);
            writeValue(field, valueList.get(0), generator, serializerProvider);
          } else {
            generator.writeArrayFieldStart(fieldName);
            for (Object subValue : valueList) {
              writeValue(field, subValue, generator, serializerProvider);
            }
            generator.writeEndArray();
          }
        }
      } else if (message.hasField(field) || (writeDefaultValues && !supportsFieldPresence(field) && field.getContainingOneof() == null)) {
        generator.writeFieldName(fieldName);
        writeValue(field, message.getField(field), generator, serializerProvider);
      } else if (include == Include.ALWAYS && field.getContainingOneof() == null) {
        generator.writeFieldName(fieldName);
        generator.writeNull();
      }
    }

    generator.writeEndObject();
  }

  private Function<FieldDescriptor, String> getPropertyNaming(Descriptor descriptor, SerializerProvider serializerProvider) {
    PropertyNamingCache cache = propertyNamingCache.get(descriptor);
    if (cache == null) {
      // use computeIfAbsent as a fallback since it allocates
      cache = propertyNamingCache.computeIfAbsent(
          descriptor,
          ignored -> PropertyNamingCache.forDescriptor(descriptor, config)
      );
    }

    return cache.forSerialization(serializerProvider.getConfig().getPropertyNamingStrategy());
  }

  private static boolean supportsFieldPresence(FieldDescriptor field) {
    // messages still support field presence in proto3
    return field.getJavaType() == JavaType.MESSAGE;
  }

  private static boolean writeSingleElementArraysUnwrapped(SerializerProvider config) {
    return config.isEnabled(SerializationFeature.WRITE_SINGLE_ELEM_ARRAYS_UNWRAPPED);
  }
} public class NullValueSerializer extends StdSerializer<NullValue> {

  public NullValueSerializer() {
    super(NullValue.class);
  }

  @Override
  public void serialize(NullValue value, JsonGenerator gen, SerializerProvider provider) throws IOException {
    gen.writeNull();
  }
} public class StructSerializer extends ProtobufSerializer<Struct> {
  private static final FieldDescriptor FIELDS_FIELD = Struct.getDescriptor().findFieldByName("fields");

  public StructSerializer() {
    super(Struct.class);
  }

  @Override
  public void serialize(
          Struct struct,
          JsonGenerator generator,
          SerializerProvider serializerProvider
  ) throws IOException {
    writeMap(FIELDS_FIELD, struct.getField(FIELDS_FIELD), generator, serializerProvider);
  }
} public class TimestampSerializer extends ProtobufSerializer<Timestamp> {

  public TimestampSerializer() {
    super(Timestamp.class);
  }

  @Override
  public void serialize(
          Timestamp timestamp,
          JsonGenerator generator,
          SerializerProvider serializerProvider
  ) throws IOException {
    generator.writeString(Timestamps.toString(timestamp));
  }
} public class ValueSerializer extends ProtobufSerializer<Value> {

  public ValueSerializer() {
    super(Value.class);
  }

  @Override
  public void serialize(
          Value value,
          JsonGenerator generator,
          SerializerProvider serializerProvider
  ) throws IOException {
    Map<FieldDescriptor, Object> fields = value.getAllFields();
    if (fields.isEmpty()) {
      generator.writeNull();
    } else {
      // should only have 1 entry
      for (Entry<FieldDescriptor, Object> entry : fields.entrySet()) {
        writeValue(entry.getKey(), entry.getValue(), generator, serializerProvider);
      }
    }
  }
} public class WrappedPrimitiveSerializer<T extends MessageOrBuilder> extends ProtobufSerializer<T> {

  public WrappedPrimitiveSerializer(Class<T> wrapperType) {
    super(wrapperType);
  }

  @Override
  public void serialize(
          MessageOrBuilder message,
          JsonGenerator generator,
          SerializerProvider serializerProvider
  ) throws IOException {
    FieldDescriptor field = message.getDescriptorForType().findFieldByName("value");
    Object value = message.getField(field);
    writeValue(field, value, generator, serializerProvider);
  }
}
import com.fasterxml.jackson.annotation.JsonAutoDetect;
import com.fasterxml.jackson.annotation.JsonAutoDetect.Visibility;
import com.fasterxml.jackson.core.Version;
import com.fasterxml.jackson.databind.JsonDeserializer;
import com.fasterxml.jackson.databind.Module;
import com.fasterxml.jackson.databind.module.SimpleDeserializers;
import com.fasterxml.jackson.databind.module.SimpleSerializers;
import com.google.protobuf.BoolValue;
import com.google.protobuf.BytesValue;
import com.google.protobuf.DoubleValue;
import com.google.protobuf.Duration;
import com.google.protobuf.ExtensionRegistry;
import com.google.protobuf.FieldMask;
import com.google.protobuf.FloatValue;
import com.google.protobuf.Int32Value;
import com.google.protobuf.Int64Value;
import com.google.protobuf.ListValue;
import com.google.protobuf.Message;
import com.google.protobuf.MessageOrBuilder;
import com.google.protobuf.NullValue;
import com.google.protobuf.StringValue;
import com.google.protobuf.Struct;
import com.google.protobuf.Timestamp;
import com.google.protobuf.UInt32Value;
import com.google.protobuf.UInt64Value;
import com.google.protobuf.Value;
import com.hubspot.jackson.datatype.protobuf.builtin.deserializers.DurationDeserializer;
import com.hubspot.jackson.datatype.protobuf.builtin.deserializers.FieldMaskDeserializer;
import com.hubspot.jackson.datatype.protobuf.builtin.deserializers.ListValueDeserializer;
import com.hubspot.jackson.datatype.protobuf.builtin.deserializers.NullValueDeserializer;
import com.hubspot.jackson.datatype.protobuf.builtin.deserializers.StructDeserializer;
import com.hubspot.jackson.datatype.protobuf.builtin.deserializers.TimestampDeserializer;
import com.hubspot.jackson.datatype.protobuf.builtin.deserializers.ValueDeserializer;
import com.hubspot.jackson.datatype.protobuf.builtin.deserializers.WrappedPrimitiveDeserializer;
import com.hubspot.jackson.datatype.protobuf.builtin.serializers.DurationSerializer;
import com.hubspot.jackson.datatype.protobuf.builtin.serializers.FieldMaskSerializer;
import com.hubspot.jackson.datatype.protobuf.builtin.serializers.ListValueSerializer;
import com.hubspot.jackson.datatype.protobuf.builtin.serializers.MessageSerializer;
import com.hubspot.jackson.datatype.protobuf.builtin.serializers.NullValueSerializer;
import com.hubspot.jackson.datatype.protobuf.builtin.serializers.StructSerializer;
import com.hubspot.jackson.datatype.protobuf.builtin.serializers.TimestampSerializer;
import com.hubspot.jackson.datatype.protobuf.builtin.serializers.ValueSerializer;
import com.hubspot.jackson.datatype.protobuf.builtin.serializers.WrappedPrimitiveSerializer;
package com.hubspot.jackson.datatype.protobuf;


/**
 * Module to add support for reading and writing Protobufs
 *
 * Register with Jackson via {@link com.fasterxml.jackson.databind.ObjectMapper#registerModule}
 */
public class ProtobufModule extends Module {
  private final ProtobufJacksonConfig config;

  public ProtobufModule() {
    this(ProtobufJacksonConfig.builder().build());
  }

  /**
   * @deprecated use {@link #ProtobufModule(ProtobufJacksonConfig)} instead
   */
  @Deprecated
  public ProtobufModule(ExtensionRegistry extensionRegistry) {
    this(ProtobufJacksonConfig.builder().extensionRegistry(extensionRegistry).build());
  }

  public ProtobufModule(ProtobufJacksonConfig config) {
    this.config = config;
  }

  @Override
  public String getModuleName() {
    return "ProtobufModule";
  }

  @Override
  public Version version() {
    return Version.unknownVersion();
  }

  @Override
  public void setupModule(SetupContext context) {
    SimpleSerializers serializers = new SimpleSerializers();
    serializers.addSerializer(new MessageSerializer(config));
    serializers.addSerializer(new DurationSerializer());
    serializers.addSerializer(new FieldMaskSerializer());
    serializers.addSerializer(new ListValueSerializer());
    serializers.addSerializer(new NullValueSerializer());
    serializers.addSerializer(new StructSerializer());
    serializers.addSerializer(new TimestampSerializer());
    serializers.addSerializer(new ValueSerializer());
    serializers.addSerializer(new WrappedPrimitiveSerializer<>(DoubleValue.class));
    serializers.addSerializer(new WrappedPrimitiveSerializer<>(FloatValue.class));
    serializers.addSerializer(new WrappedPrimitiveSerializer<>(Int64Value.class));
    serializers.addSerializer(new WrappedPrimitiveSerializer<>(UInt64Value.class));
    serializers.addSerializer(new WrappedPrimitiveSerializer<>(Int32Value.class));
    serializers.addSerializer(new WrappedPrimitiveSerializer<>(UInt32Value.class));
    serializers.addSerializer(new WrappedPrimitiveSerializer<>(BoolValue.class));
    serializers.addSerializer(new WrappedPrimitiveSerializer<>(StringValue.class));
    serializers.addSerializer(new WrappedPrimitiveSerializer<>(BytesValue.class));

    context.addSerializers(serializers);

    context.addDeserializers(new MessageDeserializerFactory(config));
    SimpleDeserializers deserializers = new SimpleDeserializers();
    deserializers.addDeserializer(Duration.class, new DurationDeserializer());
    deserializers.addDeserializer(FieldMask.class, new FieldMaskDeserializer());
    deserializers.addDeserializer(ListValue.class, new ListValueDeserializer().buildAtEnd());
    deserializers.addDeserializer(NullValue.class, new NullValueDeserializer());
    deserializers.addDeserializer(Struct.class, new StructDeserializer().buildAtEnd());
    deserializers.addDeserializer(Timestamp.class, new TimestampDeserializer());
    deserializers.addDeserializer(Value.class, new ValueDeserializer().buildAtEnd());