解组字符串 json

问题描述

我将我的数据存储在一个 redis 数据库中,当我要求它时,我得到了一个有效的 json。 json 如下所示:

{"Data":"hi","Hash":"000f7dcfca98450a0f405384db3878c1956cb98309e63cf2d0a963cff9f17260","PrevHash":"000daf177434acd55a3284787b793a3453c3d70eacdb9a84f5faed43adb2ff58","Nonce":8504,"TimeStamp":1611498968}

这是一个有效的 json 字符串(Go 省略了前导和尾引号),但在解组时出现此错误illegal base64 data at input byte 0

err = json.Unmarshal([]byte(item),&block)

type Block struct {
    Data      []byte
    Hash      []byte
    PrevHash  []byte
    Nonce     int
    TimeStamp int64
}

func (chain *BlockChain) AddBlock(data string) {
    var lastHash []byte

    item,err := chain.Database.Get(ctx,"lastHash").Result()
    Handle(err)
    lastHash = []byte(item)
    newBlock := createBlock(data,lastHash)

    _,err = chain.Database.Set(ctx,StrHash(newBlock.Hash),newBlock,0).Result()
    Handle(err)
    _,"lastHash",newBlock.Hash,0).Result()
}

func (chain *BlockChain) Iterator() *BlockChainIterator {
    return &BlockChainIterator{
        CurrentHash: chain.LastHash,Database:    chain.Database,}
}

func (iterator *BlockChainIterator) Next() *Block {
    var block *Block

    item,err := iterator.Database.Get(ctx,StrHash(iterator.CurrentHash)).Result()
    
    Handle(err)
    err = json.Unmarshal([]byte(item),&block)

    Handle(err)
    iterator.CurrentHash = block.PrevHash

    return block

}

// -----------------
// Utility Functions
// -----------------

// Todo: Think about this. Wouldn't it be better to store everything as strings rather than converting it?
type jsonBlock struct {
    Data      string
    Hash      string
    PrevHash  string
    Nonce     int
    TimeStamp int64
}

// This function gets called automatically by go-redis
func (b *Block) MarshalBinary() ([]byte,error) {
    jb := jsonBlock{
        Data:      string(b.Data),Hash:      StrHash(b.Hash),PrevHash:  StrHash(b.PrevHash),Nonce:     b.Nonce,TimeStamp: b.TimeStamp,}
    return json.Marshal(jb)
}

解决方法

JSON 中的 Hash 值(以及 PrevHash)不是数据的 Base64 编码形式,而是十六进制表示。如果您尝试将其解组为 []byte 类型的 Go 值,encoding/json 包会假定并尝试将其解释为 Base64 编码数据。

同样适用于 Data 字段:它应该是 Go 类型 string,因为它不是 Base64 编码形式,因此将其更改为 string

尝试将 HashPrevHash 解组为 string 类型的字段,并使用 hex.DecodeString() 进行六进制解码。

您可以实现自定义解组器来自动执行此操作。

一种方便的方法是创建一个 HexData 类型,它是一个 []byte 但从十六进制字符串解码:

type HexData []byte

func (h *HexData) UnmarshalJSON(data []byte) error {
    var s string
    if err := json.Unmarshal(data,&s); err != nil {
        return err
    }
    decoded,err := hex.DecodeString(s)
    if err != nil {
        return err
    }
    *h = HexData(decoded)
    return nil
}

使用:

type Block struct {
    Data      string
    Hash      HexData
    PrevHash  HexData
    Nonce     int
    TimeStamp int64
}

func main() {
    var b Block
    if err := json.Unmarshal([]byte(src),&b); err != nil {
        panic(err)
    }
    fmt.Printf("%+v\n",b)
}

输出(在 Go Playground 上尝试):

{Data:hi Hash:[0 15 125 207 202 152 69 10 15 64 83 132 219 56 120 193 149 108 185 131 9 230 60 242 208 169 99 207 249 241 114 96] PrevHash:[0 13 175 23 116 52 172 213 90 50 132 120 123 121 58 52 83 195 215 14 172 219 154 132 245 250 237 67 173 178 255 88] Nonce:8504 TimeStamp:1611498968}