Protobuf输出合法JSON适配Redshift JSON查询的可行方案咨询
解决方案
最低改造成本方案(无需调整Protobuf定义)
你现在拿到的单引号数组不需要调整上游结构,直接在Redshift侧做简单转换即可处理多Resource场景:
- 先通过
REPLACE函数把字段内的单引号替换为双引号 - 用
JSON_PARSE转为合法JSON类型,再通过数组下标提取对应Resource的字段
示例代码:
-- 提取第一个Resource的amount字段 SELECT JSON_EXTRACT_PATH_TEXT( JSON_EXTRACT_ARRAY_ELEMENT_TEXT(JSON_PARSE(REPLACE(balance, '''', '"')), 0), 'amount' ) FROM your_table;
如果需要遍历所有Resource,可以结合Redshift的GENERATE_SERIES函数展开数组。
Protobuf定义层面的改造方案
方案1:使用map类型(适合同一币种仅存在一条记录的场景)
如果你的业务逻辑中每个currency对应唯一的amount,可以直接把repeated字段改为map类型,Protobuf定义如下:
message Economy { map<string, int64> balance = 90; // key为currency标识,value为对应金额 }
序列化后输出的JSON为标准对象结构:{"euros": 916, "dolar": 112},无需任何转换即可直接用JSON_EXTRACT_PATH_TEXT提取对应币种的金额。
缺点是不支持同币种存在多条Resource的场景。
方案2:存序列化后的JSON字符串(兼容所有场景,保留格式校验)
你担心的string类型无法约束格式的问题可以通过Java端前置校验解决:
- Protobuf定义调整为string类型的balance字段
- Java端写入前先构造标准的
List<Resource>做格式校验,再通过Protobuf官方的JsonFormat工具序列化为合法JSON字符串存入字段
Protobuf定义:
message Economy { string balance = 90; // 存储序列化后的标准JSON数组 }
Java端写入示例:
import com.google.protobuf.util.JsonFormat; // 先构造并校验Resource列表符合业务要求 List<Resource> resourceList = Arrays.asList( Resource.newBuilder().setAmount(916).setCurrency("euros").build(), Resource.newBuilder().setAmount(112).setCurrency("dolar").build() ); // 序列化为标准JSON字符串 String balanceJson = JsonFormat.printer() .omittingInsignificantWhitespace() .print(EconomyOuterClass.Economy.newBuilder().addAllBalance(resourceList).build()); // 写入最终的Economy对象 EconomyOuterClass.Economy finalEconomy = EconomyOuterClass.Economy.newBuilder().setBalance(balanceJson).build();
该方案写入的JSON完全符合标准,Redshift侧可直接调用JSON函数处理,同时Java端的Resource结构校验可以完全避免非法内容入库。
内容的提问来源于stack exchange,提问作者mrc
相关产品推荐
相关产品推荐

