【问题标题】:How to flatten out a nested json structure in go如何在go中展平嵌套的json结构
【发布时间】:2020-03-13 00:22:59
【问题描述】:

我从 mongo 获取嵌套数据,我想将其展平在一个结构中以将其存储在 csv 文件中。

数据如下:

{
    "_id" : "bec7bfaa-7a47-4f61-a463-5966a2b5c8ce",
    "data" : {
        "driver" : {
            "etaToStore" : 156
        },
        "createdAt" : 1532590052,
        "_id" : "07703a33-a3c3-4ad5-9e06-d05063474d8c"
    }
} 

而我最终想要得到的结构应该是这样的

type EventStruct struct {
    Id                  string      `bson:"_id"`
    DataId              string      `bson:"data._id"`
    EtaToStore          string      `bson:"data.driver.etaToStore"`
    CreatedAt           int         `bson:"data.createdAt"`
}

这不起作用,所以我按照some SO answers 将其分解为多个结构:

// Creating a structure for the inner struct that I will receive from the query
type DriverStruct struct {
    EtaToStore  int     `bson:"etaToStore"`
}

type DataStruct struct {
    Id          string `bson:"_id"`
    Driver      DriverStruct `bson:"driver"`
    CreatedAt   int `bson:"createdAt"`
}
// Flattenning out the structure & getting the fields we need only
type EventStruct struct {
    Id                  string      `bson:"_id"`
    Data                DataStruct  `bson:"data"`
}

这会从 Mongo 查询结果中获取所有数据,但它是嵌套的:

{
  "Id": "bec7bfaa-7a47-4f61-a463-5966a2b5c8ce",
  "Data": {
     "Id": a33-a3c3-4ad5-9e06-d05063474d8c,
     "Driver": {
        "EtaToStore": 156
     },
     "CreatedAt": 1532590052
  }
}

我想结束的是:

{
  "Id": "bec7bfaa-7a47-4f61-a463-5966a2b5c8ce",
  "DataId": "a33-a3c3-4ad5-9e06-d05063474d8c",
  "EtaToStore": 156,
  "CreatedAt": 1532590052
}

我确信有一个简单的方法可以做到这一点,但我无法弄清楚,帮助!

【问题讨论】:

  • 为什么投反对票:/?

标签: go bson


【解决方案1】:

您可以实现json.Unmarshaler 接口以添加自定义方法来解组json。然后在该方法中,您可以使用嵌套结构格式,但在最后返回扁平的。

func (es *EventStruct) UnmarshalJSON(data []byte) error {
    // define private models for the data format

    type driverInner struct {
        EtaToStore int `bson:"etaToStore"`
    }

    type dataInner struct {
        ID        string      `bson:"_id" json:"_id"`
        Driver    driverInner `bson:"driver"`
        CreatedAt int         `bson:"createdAt"`
    }

    type nestedEvent struct {
        ID   string    `bson:"_id"`
        Data dataInner `bson:"data"`
    }

    var ne nestedEvent

    if err := json.Unmarshal(data, &ne); err != nil {
        return err
    }

    // create the struct in desired format
    tmp := &EventStruct{
        ID:         ne.ID,
        DataID:     ne.Data.ID,
        EtaToStore: ne.Data.Driver.EtaToStore,
        CreatedAt:  ne.Data.CreatedAt,
    }

    // reassign the method receiver pointer
    // to store the values in the struct
    *es = *tmp
    return nil
}

可运行示例:https://play.golang.org/p/83VHShfE5rI

【讨论】:

  • 感谢 Zak,这是一个非常干净的解决方案
  • 运行链接中的代码示例返回:{ID: DataID:07703a33-a3c3-4ad5-9e06-d05063474d8c EtaToStore:156 CreatedAt:1532590052}。顶级 ID 的值未显示预期值“bec7bfaa-7a47-4f61-a463-5966a2b5c8ce”
【解决方案2】:

这个问题已经有一年半的历史了,但是我今天在对 API 更新做出反应时遇到了这个问题,这让我处于同样的境地,所以这是我的解决方案(诚然,我没有用 @987654322 测试过@,但我假设 jsonbson 字段标签读取器实现以相同的方式处理它们)

嵌入式(有时称为匿名)字段可以捕获 JSON,因此您可以将多个结构组合成一个复合结构,其行为类似于单个结构。

{
    "_id" : "bec7bfaa-7a47-4f61-a463-5966a2b5c8ce",
    "data" : {
        "driver" : {
            "etaToStore" : 156
        },
        "createdAt" : 1532590052,
        "_id" : "07703a33-a3c3-4ad5-9e06-d05063474d8c"
    }
} 
type DriverStruct struct {
    EtaToStore          string      `bson:"etaToStore"`

type DataStruct struct {
    DriverStruct                    `bson:"driver"`
    DataId              string      `bson:"_id"`
    CreatedAt           int         `bson:"createdAt"`
}

type EventStruct struct {
    DataStruct                      `bson:"data"`
    Id                  string      `bson:"_id"`
}

您可以访问嵌入结构的嵌套字段,就好像父结构包含等效字段一样,例如EventStructInstance.EtaToStore 是获取它们的有效方式。

好处:

  • 您不必实现MarshallerUnmarshaller 接口,这对于这个问题来说有点矫枉过正
  • 不需要在中间结构之间复制任何字段
  • 免费处理编组和解组

Read more about embedded fields here.

【讨论】:

  • 这正是我想要的。谢谢!
【解决方案3】:

您可以使用与以下基本相同的逻辑:

package utils

// FlattenIntegers flattens nested slices of integers
func FlattenIntegers(slice []interface{}) []int {
    var flat []int

    for _, element := range slice {
        switch element.(type) {
        case []interface{}:
            flat = append(flat, FlattenIntegers(element.([]interface{}))...)
        case []int:
            flat = append(flat, element.([]int)...)
        case int:
            flat = append(flat, element.(int))
        }
    }

    return flat
}

(来源:https://gist.github.com/Ullaakut/cb1305ede48f2391090d57cde355074f

通过调整它以适应 JSON 中的内容。如果您希望它是通用的,那么您需要支持它可以包含的所有类型。

【讨论】:

    猜你喜欢
    • 2019-01-21
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-02-02
    • 1970-01-01
    • 1970-01-01
    • 2021-11-19
    • 2014-08-29
    相关资源
    最近更新 更多