我如何将复杂的JSON映射到其他JSON

时间:2017-07-12 19:04:39

标签: go

我正在尝试为我使用的所有第三方API构建聚合服务, 此聚合服务从我的主系统获取json值,它将此值设置为等效于第三方api密钥的密钥,然后,聚合服务它将向第三方发送请求新的json格式的api。

示例1:

package main

import (
    "encoding/json"
    "fmt"
    "log"

    "github.com/tidwall/gjson"
)

func main() {
    // mapping JSON
    mapB := []byte(`
    {
        "date": "createdAt",
        "clientName": "data.user.name"
    }
    `)

    // from my main system
    dataB := []byte(`
    {
        "createdAt": "2017-05-17T08:52:36.024Z",
        "data": {
            "user": {
                "name": "xxx"
            }
        }
    }
    `)

    mapJSON := make(map[string]interface{})
    dataJSON := make(map[string]interface{})
    newJSON := make(map[string]interface{})

    err := json.Unmarshal(mapB, &mapJSON)
    if err != nil {
        log.Panic(err)
    }

    err = json.Unmarshal(dataB, &dataJSON)
    if err != nil {
        log.Panic(err)
    }

    for i := range mapJSON {
        r := gjson.GetBytes(dataB, mapJSON[i].(string))
        newJSON[i] = r.Value()
    }

    newB, err := json.MarshalIndent(newJSON, "", "  ")
    if err != nil {
        log.Println(err)
    }

    fmt.Println(string(newB))
}

输出:

{
  "clientName": "xxx",
  "date": "2017-05-17T08:52:36.024Z"
}

我使用gjson包从json文档中以简单的方式从我的主系统请求中获取值。

示例-2:

import (
    "encoding/json"
    "fmt"
    "log"

    "github.com/tidwall/gjson"
)

func main() {
    // mapping JSON
    mapB := []byte(`
    {
        "date": "createdAt",
        "clientName": "data.user.name",
        "server":{
            "google":{
                "date" :"createdAt"
            }
        }
    }
    `)

    // from my main system
    dataB := []byte(`
    {
        "createdAt": "2017-05-17T08:52:36.024Z",
        "data": {
            "user": {
                "name": "xxx"
            }
        }
    }
    `)

    mapJSON := make(map[string]interface{})
    dataJSON := make(map[string]interface{})
    newJSON := make(map[string]interface{})

    err := json.Unmarshal(mapB, &mapJSON)
    if err != nil {
        log.Panic(err)
    }

    err = json.Unmarshal(dataB, &dataJSON)
    if err != nil {
        log.Panic(err)
    }

    for i := range mapJSON {
        r := gjson.GetBytes(dataB, mapJSON[i].(string))
        newJSON[i] = r.Value()
    }

    newB, err := json.MarshalIndent(newJSON, "", "  ")
    if err != nil {
        log.Println(err)
    }

    fmt.Println(string(newB))
}

输出:

panic: interface conversion: interface {} is map[string]interface {}, not string

我可以使用https://golang.org/ref/spec#Type_assertions来处理这个错误,但是如果这个json对象有数组并且在这个数组中有json对象....

我的问题是我有不同的api,每个api都有自己的json架构,和我的映射json的方式只能工作 第三方api只有json键值,在这个数组json对象中没有嵌套的json或数组。

有没有办法映射复杂的json架构,或Golang包来帮助我做到这一点?

1 个答案:

答案 0 :(得分:2)

修改

评论互动和更新后的问题。在我们前进之前,我想提一下。

  

我只是看着你的example-2记住一件事。映射是从一种形式到另一种形式。基本上one known format to targeted format。必须处理每种数据类型。您无法在逻辑上执行genericgeneric映射(但技术上可行,但需要更多时间和努力,您可以在此处进行操作)。

我创建了一种方法的样本工作程序;它执行源到目标格式的映射。请参考此计划作为起点,并利用您的创造力来实施您的计划。

游乐场链接:https://play.golang.org/p/MEk_nGcPjZ

说明:示例程序实现了两种不同的源格式到一种目标格式。该计划包括 -

  • 提供商1的目标映射定义
  • 提供商2的目标映射定义
  • 提供商1 JSON
  • 提供商2 JSON
  • 映射功能
  • 有针对性的JSON元帅

程序中的关键元素:请参阅完整程序的播放链接。

type MappingInfo struct {
    TargetKey     string
    SourceKeyPath string
    DataType      string
}

地图功能:

func mapIt(mapping []*MappingInfo, parsedResult gjson.Result) map[string]interface{} {
    mappedData := make(map[string]interface{})
    for _, m := range mapping {
        switch m.DataType {
        case "time":
            mappedData[m.TargetKey] = parsedResult.Get(m.SourceKeyPath).Time()
        case "string":
            mappedData[m.TargetKey] = parsedResult.Get(m.SourceKeyPath).String()
        }
    }
    return mappedData
}

输出

Provider 1 Result: map[date:2017-05-17 08:52:36.024 +0000 UTC clientName:provider1 username]
Provider 1 JSON: {
  "clientName": "provider1 username",
  "date": "2017-05-17T08:52:36.024Z"
}

Provider 2 Result: map[date:2017-05-12 06:32:46.014 +0000 UTC clientName:provider2 username]
Provider 2 JSON: {
  "clientName": "provider2 username",
  "date": "2017-05-12T06:32:46.014Z"
}
祝你好运,编码愉快!

通常将一个结构转换/转换为另一个结构,您必须使用应用程序逻辑来处理它。

正如你在问题中提到的那样:

  

我的问题是我有不同的api,每个api都有自己的json架构

每个aggregation系统都是如此。

有效处理此要求的一种方法;是为每个提供者JSON结构和目标JSON结构保持键的映射。

例如:这是一种方法,请根据您的需要选择您的设计。

各种提供商的JSON结构:

// Provider 1 : JSON structrure
{
  "createdAt": "2017-05-17T08:52:36.024Z",
  "data": {
    "user": {
      "name": "xxx"
    }
  }
}

// Provider 2 : JSON structrure
{
  "username": "yyy"
  "since": "2017-05-17T08:52:36.024Z",
}

目标JSON结构的映射:

jsonMappingByProvider := make(map[string]string)

// Targeted Mapping for Provider 1
jsonMappingByProvider["provider1"] = `
{
    "date": "createdAt",
    "clientName": "data.user.name"
}
`

// Targeted Mapping for Provider 2
jsonMappingByProvider["provider2"] = `
{
    "date": "since",
    "clientName": "username"
}
`

现在,根据您正在处理的提供程序,获取映射并将响应JSON映射到目标结构中。

// get the mapping info by provider
mapping := jsonMappingByProvider["provider1"]

// Parse the response JSON 
// Do the mapping

通过这种方式,您可以有效地控制每个提供商及其映射。