/* * Copyright 2025 Google. This software is provided as-is, without warranty or representation for any use or purpose. * Your use of it is subject to your agreement with Google. */ package com.example.util; import com.google.protobuf.ListValue; import com.google.protobuf.NullValue; import com.google.protobuf.Struct; import com.google.protobuf.Value; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import java.util.stream.Collectors; public class ProtobufUtil { private static final Logger logger = LoggerFactory.getLogger(ProtobufUtil.class); /** * Converts a Java Object to a Protobuf Value. * Supports primitive types, String, Map, and List. * Maps will be converted to Protobuf Structs. * Lists will be converted to Protobuf ListValues. */ @SuppressWarnings("rawtypes") public static Value convertJavaObjectToProtobufValue(Object obj) { if (obj == null) { return Value.newBuilder().setNullValue(NullValue.NULL_VALUE).build(); } else if (obj instanceof Boolean) { return Value.newBuilder().setBoolValue((Boolean) obj).build(); } else if (obj instanceof Integer) { return Value.newBuilder().setNumberValue(((Integer) obj).doubleValue()).build(); } else if (obj instanceof Long) { return Value.newBuilder().setNumberValue(((Long) obj).doubleValue()).build(); } else if (obj instanceof Double) { return Value.newBuilder().setNumberValue((Double) obj).build(); } else if (obj instanceof String) { return Value.newBuilder().setStringValue((String) obj).build(); } else if (obj instanceof Enum) { return Value.newBuilder().setStringValue(((Enum) obj).name()).build(); } else if (obj instanceof Map) { Struct.Builder structBuilder = Struct.newBuilder(); ((Map) obj).forEach((key, val) -> structBuilder.putFields(String.valueOf(key), convertJavaObjectToProtobufValue(val)) ); return Value.newBuilder().setStructValue(structBuilder.build()).build(); } else if (obj instanceof List) { ListValue.Builder listValueBuilder = ListValue.newBuilder(); ((List) obj).forEach(item -> listValueBuilder.addValues(convertJavaObjectToProtobufValue(item)) ); return Value.newBuilder().setListValue(listValueBuilder.build()).build(); } logger.warn("Unsupported type for Protobuf conversion: {}. Converting to String.", obj.getClass().getName()); return Value.newBuilder().setStringValue(obj.toString()).build(); } /** * Converts a Protobuf Value to a Java Object. * Supports Null, Boolean, Number, String, Struct (to Map), and ListValue (to List). */ public static Object convertProtobufValueToJavaObject(Value protobufValue) { return switch (protobufValue.getKindCase()) { case NULL_VALUE -> null; case BOOL_VALUE -> protobufValue.getBoolValue(); case NUMBER_VALUE -> protobufValue.getNumberValue(); case STRING_VALUE -> protobufValue.getStringValue(); case STRUCT_VALUE -> protobufValue.getStructValue().getFieldsMap().entrySet().stream() .collect(Collectors.toMap( Map.Entry::getKey, entry -> convertProtobufValueToJavaObject(entry.getValue()), (oldValue, newValue) -> oldValue, LinkedHashMap::new )); case LIST_VALUE -> protobufValue.getListValue().getValuesList().stream() .map(ProtobufUtil::convertProtobufValueToJavaObject) // Use static method reference .collect(Collectors.toList()); default -> { logger.warn("Unsupported Protobuf Value type: {}. Returning null.", protobufValue.getKindCase()); yield null; } }; } }