简体   繁体   English

在Go中维护未解析的JSON字段的最佳方法是什么?

[英]What's the best way to maintain un-parsed JSON fields in Go?

I'd like to decode a JSON blob into a Go struct, manipulate on it, and encode it back to JSON. 我想将JSON blob解码为Go结构,对其进行操作,然后将其编码回JSON。 However, there are dynamic fields in the JSON that aren't relevant to my struct and I want to maintain them when I serialize back to JSON. 但是,JSON中的动态字段与我的struct无关,我想在序列化回JSON时维护它们。

For example: 例如:

{ "name": "Joe Smith", 
  "age": 42, 
  "phone": "614-555-1212", 
  "debug": True, 
  "codeword": "wolf" }

type Person struct {
    Name string
    Age uint
    Phone string
}

var p Person
json.Unmarshal(data, &p)
// Happy birthday
p.Age++
data, _ = json.Marshal(p)

// Any way to maintain the "debug" and "codeword" fields -- which might not
// be known ahead of time?

I know one possibility is to decode everything into a map[string]interface{} but boy, do things get ugly when you do that. 我知道一种可能性是将所有内容解码为map[string]interface{}但是男孩,当你这样做时,事情变得丑陋。

Is there any way to have the best of both worlds? 有没有办法让两全其美?

With encoding/json there's no way to do decode a struct and save unknown fields at the same level for posterior re-encoding. 使用encoding/json ,无法对结构进行解码并将同一级别的未知字段保存为后重新编码。 What you can do is choose not to decode part of the struct by using the json.RawMessage type, along the lines of: 你可以做的是选择不使用json.RawMessage类型解码部分结构, 类似于

type Person struct {
    Name    string
    Address json.RawMessage
}

You might workaround that by implementing your own Unmarshaler that decodes the document into a map and saves the unknown keys in a field of the struct, and then have a counterpart Marshaler that puts the fields back before marshaling. 您可以通过实现自己的Unmarshaler解决方法,将文档解码为地图并将未知密钥保存在结构的字段中,然后让对应的Marshaler在编组之前将字段放回。

Just out of curiosity, the feature you're looking for does exist in labix.org/v2/mgo/bson , via the inline tag flag , and the goal was to solve precisely the use case you're describing. 出于好奇,您正在寻找的功能确实存在于labix.org/v2/mgo/bson中 ,通过内联标记标记 ,目标是精确地解决您正在描述的用例。

Turns out I wrote my own library to do this: https://github.com/joeshaw/json-lossless 结果我写了自己的库来做到这一点: https//github.com/joeshaw/json-lossless

It builds on top of go-simplejson , keeping the parsed JSON state in a simplejson.Json and proxying state between it and the struct whenever the struct is marshaled or unmarshaled. 它构建在go-simplejson之上,只要结构被封送或解组,就将解析后的JSON状态保持在simplejson.Json和它与struct之间的代理状态。

Example usage: 用法示例:

package main

import (
    "encoding/json"
    "fmt"
    "time"

    "github.com/joeshaw/json-lossless"
)

type Person struct {
    lossless.JSON       `json:"-"`

    Name string         `json:"name"`
    Age int             `json:"age"`
    Birthdate time.Time `json:"birthdate"`
}

func (p *Person) UnmarshalJSON(data []byte) error {
    return p.JSON.UnmarshalJSON(p, data)
}

func (p Person) MarshalJSON() []byte, error) {
    return p.JSON.MarshalJSON(p)
}

var jsondata = []byte(`
{"name": "David Von Wolf",
 "age": 33,
 "birthdate": "1980-09-16T10:44:40.295451647-04:00",
 "phone": "614-555-1212"}
`)

func main() {
    var p Person
    err := json.Unmarshal(jsondata, &p)
    if err != nil {
        panic(err)
    }

    // Set values on the struct
    p.Age++

    // Set arbitrary keys not in the struct
    p.Set("title", "Chief Wolf")

    fmt.Printf("%#v\n", p)

    data, err := json.Marshal(p)
    if err != nil {
        panic(err)
    }

    fmt.Println(string(data))
}

Prints (formatted for readability by me): 打印(格式化以供我阅读):

main.Person{JSON:lossless.JSON{json:(*simplejson.Json)(0x21020a190)}, 
            Name:"David Von Wolf", 
            Age:34, 
            Birthdate:time.Time{sec:62473560280, 
                                nsec:295451647, 
                                loc:(*time.Location)(0x16de60)}}
{"age":34,
 "birthdate":"1980-09-16T10:44:40.295451647-04:00",
 "name":"David Von Wolf",
 "phone":"614-555-1212",
 "title": "Chief Wolf"}

go-simplejson对于这类工作来说非常方便。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM