在golang中,是否有一种更简洁的方式来处理未知结构的JSON对象?

huangapple go评论80阅读模式
英文:

Is there a more concise way of using json objects of unknown structure in golang?

问题

当我有一个可靠的 REST API 端点,返回一些简单的 JSON 数据时,我可以使用一个结构体来定义预期的 JSON 结果的结构,但是有一些我必须使用的端点返回非常大且复杂的 JSON 数据,而这些结果的结构并不总是已知的。

我一直在使用以下代码进行解组:

type JsonObj map[string]interface{}

func (jo JsonObj) GetString(name string) (string, error) {
    if val, exists := jo[name]; exists {
        if v, ok := val.(string); ok {
            return v, nil
        }
        return "", errors.New(name+" is not a string")
    }
    return "", errors.New(name+" property not found")
}

func (jo JsonObj) GetFloat64(name string) (float64, error) {
    if val, exists := jo[name]; exists {
        if v, ok := val.(float64); ok {
            return v, nil
        }
        return 0, errors.New(name+" is not a float64")
    }
    return 0, errors.New(name+" property not found")
}

以及类似的方式,我还有 GetIntGetBoolGetSliceGetJsonObj 等函数。

但是你可以看到,除了类型断言参数之外,所有这些函数在内容上几乎完全相同。有没有办法通过传递类型断言参数来将所有这些函数有效地减少到一个函数?

英文:

When I have a reliable rest api endpoint that returns some simple json, I can use a struct to define exactly the structure of the expected json result, but there are certain endpoints I have to use that return very large and complex json result data, and the structure of these results are not always known.

I have been using this to unmarshal into:

type JsonObj map[string]interface{}

func (jo JsonObj) GetString(name string) (string, error) {
	if val, exists := jo[name]; exists {
		if v, ok := val.(string); ok {
			return v, nil
		}
		return "", errors.New(name+" is not a string")
	}
	return "", errors.New(name+" property not found")
}

func (jo JsonObj) GetFloat64(name string) (float64, error) {
	if val, exists := jo[name]; exists {
		if v, ok := val.(float64); ok {
			return v, nil
		}
		return 0, errors.New(name+" is not a float64")
	}
	return 0, errors.New(name+" property not found")
}

and in this same way I have GetInt, GetBool, GetSlice, GetJsonObj,

but as you can see all of these functions are virtually identical in content except for the type assertion parameter. is there a way of passing in the type assertion parameter to reduce all these functions effectively to a single function?

答案1

得分: 0

这是我目前能提供的最好的例子,非常简单:https://play.golang.org/p/U9WJ0bIJPp
我无法想象你能比这更好地压缩/提取它:

package main

import (
	"fmt"
	"errors"
)

type T interface{}
type JsonObj map[string]T

func (jo JsonObj) Type(name string, defaultVal T, typeName string, typeAsserter func(val T) (T, bool)) (T, error){
	if val, exists := jo[name]; exists {
		if v, ok := typeAsserter(val); ok {
			return v, nil
		}
		return defaultVal, errors.New(name+" is not of type "+typeName)
	}
	return defaultVal, errors.New(name+" property not found")
}

func (jo JsonObj) String(name string) (string, error) {
	ret, err := jo.Type(name, "", "string", func(val T)(ret T, ok bool){ret, ok = val.(string);return})
	return ret.(string), err
}

func (jo JsonObj) Float64(name string) (float64, error) {
	ret, err := jo.Type(name, 0, "float64", func(val T)(ret T, ok bool){ret, ok = val.(float64);return})
	return ret.(float64), err
}

func (jo JsonObj) Int(name string) (int, error) {
	ret, err := jo.Type(name, 0, "int", func(val T)(ret T, ok bool){if ret, ok = val.(float64); ok {ret = int(ret.(float64))};return})
	return ret.(int), err
}

func (jo JsonObj) Bool(name string) (bool, error) {
	ret, err := jo.Type(name, false, "bool", func(val T)(ret T, ok bool){ret, ok = val.(bool);return})
	return ret.(bool), err
}

func main() {
	jo := JsonObj{
		"aString": "foo",
		"aFloat64": 3.142,
		"anInt": 42.0, //in a json string unmarshalling all numbers are float64 even "int"s
		"aBool": true,
		
	}
	fmt.Println(jo.String("aString"))
	fmt.Println(jo.Float64("aFloat64"))
	fmt.Println(jo.Int("anInt"))
	fmt.Println(jo.Bool("aBool"))
	fmt.Println(jo.String("missingString"))
	fmt.Println(jo.Bool("anInt"))
}

但正如评论中提到的,有一个库提供了更强大的处理任意 JSON 的方法,可以参考:https://github.com/bitly/go-simplejson

英文:

This is the best I've got right now, as a very simple example: https://play.golang.org/p/U9WJ0bIJPp
I can't imagine you'd be able to compress/extract it much more than this:

package main
import (
"fmt"
"errors"
)
type T interface{}
type JsonObj map[string]T
func (jo JsonObj) Type(name string, defaultVal T, typeName string, typeAsserter func(val T) (T, bool)) (T, error){
if val, exists := jo[name]; exists {
if v, ok := typeAsserter(val); ok {
return v, nil
}
return defaultVal, errors.New(name+" is not of type "+typeName)
}
return defaultVal, errors.New(name+" property not found")
}
func (jo JsonObj) String(name string) (string, error) {
ret, err := jo.Type(name, "", "string", func(val T)(ret T, ok bool){ret, ok = val.(string);return})
return ret.(string), err
}
func (jo JsonObj) Float64(name string) (float64, error) {
ret, err := jo.Type(name, 0, "float64", func(val T)(ret T, ok bool){ret, ok = val.(float64);return})
return ret.(float64), err
}
func (jo JsonObj) Int(name string) (int, error) {
ret, err := jo.Type(name, 0, "int", func(val T)(ret T, ok bool){if ret, ok = val.(float64); ok {ret = int(ret.(float64))};return})
return ret.(int), err
}
func (jo JsonObj) Bool(name string) (bool, error) {
ret, err := jo.Type(name, false, "bool", func(val T)(ret T, ok bool){ret, ok = val.(bool);return})
return ret.(bool), err
}
func main() {
jo := JsonObj{
"aString": "foo",
"aFloat64": 3.142,
"anInt": 42.0, //in a json string unmarshalling all numbers are float64 even "int"s
"aBool": true,
}
fmt.Println(jo.String("aString"))
fmt.Println(jo.Float64("aFloat64"))
fmt.Println(jo.Int("anInt"))
fmt.Println(jo.Bool("aBool"))
fmt.Println(jo.String("missingString"))
fmt.Println(jo.Bool("anInt"))
}

But as mentioned in the comments there is a library which provides a much more robust means of working with arbitrary json, https://github.com/bitly/go-simplejson

huangapple
  • 本文由 发表于 2015年10月23日 16:59:13
  • 转载请务必保留本文链接:https://go.coder-hub.com/33298489.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定