为何JsonFormat.parser()无法正确解析FieldMask?
Protobuf FieldMask解析时蛇形命名导致合并失效的解决方案
问题描述
在旧项目中使用JsonFormat.parser()解析JSON时,传入的驼峰格式FieldMask(如fullName)会被自动转换为蛇形命名(如full_name),导致FieldMaskUtil.merge()无法按预期工作。而在Spring Boot项目中通过添加ProtobufHttpMessageConverter Bean可以正常运行。
请求示例:
{ "user": { "fullName": "Abc" }, "mask": "fullName" }
解析后得到的mask值为full_name,与预期不符。
相关代码:
User byId = userRepo.getById(id); if (byId == null) { throw new NotFoundException("User not found"); } User.Builder destination = byId.toBuilder(); FieldMaskUtil.merge(updateUserRequest.getMask(), updateUserRequest.getUser(), destination);
解决方案
方案一:使用proto定义的蛇形命名传入FieldMask
Protobuf的JsonFormat默认遵循proto文件的字段命名规范(推荐蛇形命名),直接在请求中传入proto定义的字段名即可:
{ "user": { "fullName": "Abc" }, "mask": "full_name" }
这种方式无需修改后端代码,解析后的FieldMask会直接匹配proto字段名,FieldMaskUtil.merge()可正常执行。
方案二:配置JsonFormat.parser保留原始字段名(不推荐)
如果无法修改前端请求格式,可以在创建JsonFormat解析器时,配置禁用字段名转换逻辑:
JsonFormat parser = JsonFormat.parser() .preservingProtoFieldNames() .ignoringUnknownFields(); // 使用该解析器解析请求体 UpdateUserRequest request = parser.merge(jsonString, UpdateUserRequest.newBuilder()).build();
注意:preservingProtoFieldNames会让解析器严格按照proto文件的字段名匹配,可能影响其他字段的解析,需谨慎使用。
方案三:手动转换FieldMask命名格式
如果需要兼容驼峰格式的输入,可以在解析后手动将蛇形命名转换为驼峰命名,再执行合并:
User byId = userRepo.getById(id); if (byId == null) { throw new NotFoundException("User not found"); } User.Builder destination = byId.toBuilder(); // 获取解析后的蛇形FieldMask,转换为驼峰格式 FieldMask originalMask = updateUserRequest.getMask(); List<String> camelCasePaths = originalMask.getPathsList().stream() .map(this::snakeToCamel) .collect(Collectors.toList()); FieldMask camelCaseMask = FieldMask.newBuilder().addAllPaths(camelCasePaths).build(); // 使用转换后的FieldMask执行合并 FieldMaskUtil.merge(camelCaseMask, updateUserRequest.getUser(), destination);
蛇形转驼峰工具方法
private String snakeToCamel(String snakeCase) { StringBuilder sb = new StringBuilder(); boolean capitalizeNext = false; for (char c : snakeCase.toCharArray()) { if (c == '_') { capitalizeNext = true; } else { sb.append(capitalizeNext ? Character.toUpperCase(c) : c); capitalizeNext = false; } } return sb.toString(); }
内容的提问来源于stack exchange,提问作者Kshitij Dhakal
相关产品推荐
相关产品推荐

