求助:基于上层Type字段实现VerifyingKey的自定义JSON反序列化
解决方案
要实现这个需求,核心思路是给SingleKey结构体实现自定义的JSON反序列化方法——因为VerifyingKey的解析逻辑依赖上层的Type字段,单独给OtherFields写反序列化拿不到Type的值,所以必须从顶层结构体入手。
具体实现步骤如下:
- 定义临时结构体,先解析出
Type和OtherFields的大部分字段,把VerifyingKey保留为原始JSON字节(用json.RawMessage),避免提前解析出错。 - 在
SingleKey的UnmarshalJSON方法里,先将JSON解析到临时结构体,再根据Type字段的值,把VerifyingKey的原始JSON解析成对应的具体PublicKey实现类型切片。 - 针对不同密钥类型编写解析逻辑,同时处理错误场景。
完整代码示例:
package main import ( "encoding/json" "errors" "fmt" ) type SingleKey struct { Type string `json:"type"` Others OtherFields `json:"wrapped_fields"` } type OtherFields struct { FQDN string `json:"fqdn"` Address string `json:"address"` Nonce int64 `json:"nonce"` Challenge []byte `json:"challenge"` NetworkID string `json:"network_id"` VerifyingKey []PublicKey `json:"verifying_key"` Signature []byte `json:"signature"` } // PublicKey 公钥接口 type PublicKey interface { KeyType() string // 标识密钥类型,用于类型断言或区分 } // RsaPublicKey RSA公钥具体实现 type RsaPublicKey struct { Modulus string `json:"modulus"` Exponent int `json:"exponent"` } func (r RsaPublicKey) KeyType() string { return "rsa" } // EcdsaPublicKey ECDSA公钥具体实现 type EcdsaPublicKey struct { Curve string `json:"curve"` X string `json:"x"` Y string `json:"y"` } func (e EcdsaPublicKey) KeyType() string { return "ecdsa" } // 临时结构体,用于中间解析 type tempSingleKey struct { Type string `json:"type"` Others struct { FQDN string `json:"fqdn"` Address string `json:"address"` Nonce int64 `json:"nonce"` Challenge []byte `json:"challenge"` NetworkID string `json:"network_id"` VerifyingKey json.RawMessage `json:"verifying_key"` // 保留原始JSON Signature []byte `json:"signature"` } `json:"wrapped_fields"` } // 给SingleKey实现自定义JSON反序列化 func (s *SingleKey) UnmarshalJSON(data []byte) error { var temp tempSingleKey if err := json.Unmarshal(data, &temp); err != nil { return err } // 先赋值普通字段 s.Type = temp.Type s.Others.FQDN = temp.Others.FQDN s.Others.Address = temp.Others.Address s.Others.Nonce = temp.Others.Nonce s.Others.Challenge = temp.Others.Challenge s.Others.NetworkID = temp.Others.NetworkID s.Others.Signature = temp.Others.Signature // 根据Type解析VerifyingKey var verifyingKeys []PublicKey switch temp.Type { case "rsa": var rsaKeys []RsaPublicKey if err := json.Unmarshal(temp.Others.VerifyingKey, &rsaKeys); err != nil { return fmt.Errorf("解析RSA公钥失败: %w", err) } for _, key := range rsaKeys { verifyingKeys = append(verifyingKeys, key) } case "ecdsa": var ecdsaKeys []EcdsaPublicKey if err := json.Unmarshal(temp.Others.VerifyingKey, &ecdsaKeys); err != nil { return fmt.Errorf("解析ECDSA公钥失败: %w", err) } for _, key := range ecdsaKeys { verifyingKeys = append(verifyingKeys, key) } default: return errors.New("未知密钥类型: " + temp.Type) } s.Others.VerifyingKey = verifyingKeys return nil } // 测试代码 func main() { rsaJSON := `{ "type": "rsa", "wrapped_fields": { "fqdn": "example.com", "address": "192.168.1.1", "nonce": 12345, "challenge": "YWJjZA==", "network_id": "test-net", "verifying_key": [ {"modulus": "abc123", "exponent": 65537} ], "signature": "dGhpcyBpcyBhIHNpZ25hdHVyZQ==" } }` var sk SingleKey if err := json.Unmarshal([]byte(rsaJSON), &sk); err != nil { fmt.Println("反序列化失败:", err) return } // 验证解析结果 for _, key := range sk.Others.VerifyingKey { if rsaKey, ok := key.(RsaPublicKey); ok { fmt.Printf("解析得到RSA公钥: 模数=%s, 指数=%d\n", rsaKey.Modulus, rsaKey.Exponent) } } }
关键说明:
- 给
PublicKey接口添加KeyType()方法,是Go语言中接口实现的规范要求,也方便后续区分不同密钥类型。 - 临时结构体
tempSingleKey的作用是先完成所有字段的初步解析,仅保留VerifyingKey的原始JSON,确保我们能拿到Type后再针对性解析。 - 反序列化方法中先完成普通字段的赋值,再根据
Type分支处理VerifyingKey,最后转换为[]PublicKey接口切片赋值给目标结构体。 - 错误处理添加了明确的场景描述,方便调试定位问题。
内容的提问来源于stack exchange,提问作者Erdem Tuna
相关产品推荐
相关产品推荐

