如何定义并解析映射Google Protobuf Oneof字段的GraphQL联合类型?
问题:Protobuf Oneof 转 GraphQL 联合类型的解析方案
环境
- 前端:nestJS、apollo graphQL、react typescript
- 后端:protobuf messages、java
问题场景
后端通过Protobuf的oneof类型封装数据返回,前端需要在GraphQL中把这些数据解析成对应的联合类型,但遇到了一个问题:后端返回的ContentWrapper对象没有统一的content字段,只有digest、alert、generic这几个可能的子字段,常规的联合类型resolveType写法无法直接使用。
示例代码
Protobuf 定义
message Digest { int32 digestId = 1; string digestContent = 2; } message Alert { string alertMsg = 1; } message ContentWrapper { oneof content { Digest digest = 1; Alert alert = 2; string generic = 3; } }
后端返回数据示例
[ { digest: { digestId: 4588283, digestContent: 'testmsgasdasdasd' }, alert: undefined, generic: undefined }, { alert: { alertMsg: 'testmsgasdasdasd' }, digest: undefined, generic: undefined } ]
GraphQL 类型定义
type ContentWrapper { content: ContentUnion } union ContentUnion = Digest | Alert | String type Digest { digestId: Int digestMsg: String } type Alert { alertMsg: String }
解决方法
1. 标准解析流程(推荐)
先处理ContentWrapper的content字段提取,再针对提取后的数据编写resolveType,逻辑更清晰:
第一步:提取ContentWrapper的有效内容
在ContentWrapper的解析器中,从对象里取出第一个非undefined的子字段作为content的返回值:
// ContentWrapper解析器 const ContentWrapperResolver = { content: (obj) => { if (obj.digest !== undefined) return obj.digest; if (obj.alert !== undefined) return obj.alert; if (obj.generic !== undefined) return obj.generic; return null; } };
第二步:编写ContentUnion的resolveType
此时resolveType接收的obj是提取后的具体数据(比如Digest对象、Alert对象或字符串),可以直接根据属性或类型判断:
// ContentUnion的resolveType const ContentUnionResolver = { __resolveType(obj) { if (obj.digestId) return 'Digest'; if (obj.alertMsg) return 'Alert'; if (typeof obj === 'string') return 'String'; throw new Error('无法识别的ContentUnion类型'); } };
2. 简化方案:直接处理ContentWrapper对象
如果不想额外处理content字段的提取,也可以让resolveType直接接收整个ContentWrapper对象,直接判断其中的字段:
const ContentUnionResolver = { __resolveType(obj) { if (obj.digest !== undefined) return 'Digest'; if (obj.alert !== undefined) return 'Alert'; if (obj.generic !== undefined) return 'String'; throw new Error('无法识别的ContentUnion类型'); } };
3. 扩展性优化(适配未来新增类型)
如果后续Protobuf的oneof可能新增成员,可以用映射表简化维护,避免重复修改判断逻辑:
// 定义字段与GraphQL类型的映射表 const typeMapping = { digest: 'Digest', alert: 'Alert', generic: 'String' }; // ContentWrapper的content解析器 const ContentWrapperResolver = { content: (obj) => { for (const key of Object.keys(typeMapping)) { if (obj[key] !== undefined) return obj[key]; } return null; } }; // ContentUnion的resolveType const ContentUnionResolver = { __resolveType(obj) { if (obj.digestId) return 'Digest'; if (obj.alertMsg) return 'Alert'; if (typeof obj === 'string') return 'String'; throw new Error('无法识别的ContentUnion类型'); } };
内容的提问来源于stack exchange,提问作者apricojuice
相关产品推荐
相关产品推荐

