Golang如何接受不确定的值?

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

How does Golang accept uncertain values

问题

后端返回的值是不固定的,有时候是这样的:

{"application": {"instance": [{"instanceId": "v1"}, {"instanceId": "v2"}]}}

有时候是这样的:

{"application": {"instance": {"instanceId": "v"}}}

我应该如何提取对应的instanceId值?

package main

import (
	"encoding/json"
	"fmt"
)

type Application struct {
	Application struct {
		Instance json.RawMessage `json:"instance"`
	} `json:"application"`
}

func main() {
	a := `{"application": {"instance": {"instanceId": "v"}}}`
	//a := `{"application": {"instance": [{"instanceId": "v1"}, {"instanceId": "v2"}]}}`
	var p Application
	errJson := json.Unmarshal([]byte(a), &p)
	if errJson != nil {
		fmt.Printf("errJson")
	}
	fmt.Printf("type:%T", p.Application.Instance)
}
英文:

Back-end return values are not fixed, sometimes:

{"application": {"instance": [{"instanceId": "v1"}, {"instanceId": "v2"}]}}

or sometimes:

{"application": {"instance": {"instanceId": "v"}}}

how should I take out the corresponding instanceId value?

package main

import (
	"encoding/json"
	"fmt"
)

type Application struct {
	Application struct {
		Instance json.RawMessage `json:"instance"`
	} `json:"application"`
}


func main() {
	a := `{"application": {"instance": {"instanceId": "v"}}}`
	//a := `{"application": {"instance": [{"instanceId": "v1"}, {"instanceId": "v2"}]}} `
	var p Application
	errJson := json.Unmarshal([]byte(a), &p)
	if errJson != nil {
		fmt.Printf("errJson")
	}
	fmt.Printf("type:%T", p.Application.Instance)

}

答案1

得分: 2

由于两个值类型冲突(一个是结构体,另一个是结构体切片),将其封装为单个类型变得混乱,即使使用interface{}等通用解决方案也无法解决。

最简单的解决方案是定义两个不同的类型,并将其解组为其中一个以查看哪个“有效”:

func unmarsh(body []byte) (*type1, *type2, error) {
    var (
        t1 type1
        t2 type2
    )

    err := json.Unmarshal(body, &t1)
    if err == nil {
        return &t1, nil, nil
    }

    err = json.Unmarshal(body, &t2)
    if err == nil {
        return nil, &t2, nil
    }

    return nil, nil, err
}

在你的示例中,这两个类型将是:

type type1 struct {
    Application struct {
        Instance []struct {
            InstanceID string `json:"instanceId"`
        } `json:"instance"`
    } `json:"application"`
}

type type2 struct {
    Application struct {
        Instance struct {
            InstanceID string `json:"instanceId"`
        } `json:"instance"`
    } `json:"application"`
}

工作示例:
https://play.golang.org/p/Kma32gWfghb

英文:

Since the 2 value types clash (one a struct another a slice of structs) it gets messy to encapsulate this into a single type even using catch-all solutions like interface{}.

The simplest solution is present two distinct types and marshal into either to see which "works":

func unmarsh(body []byte) (*type1, *type2, error) {
	var (
		t1 type1
		t2 type2
	)

	err := json.Unmarshal(body, &t1)
	if err == nil {
		return &t1, nil, nil
	}

	err = json.Unmarshal(body, &t2)
	if err == nil {
		return nil, &t2, nil
	}

	return nil, nil, err

}

and in your example the two types would be:

type type1 struct {
	Application struct {
		Instance []struct {
			InstanceID string `json:"instanceId"`
		} `json:"instance"`
	} `json:"application"`
}

type type2 struct {
	Application struct {
		Instance struct {
			InstanceID string `json:"instanceId"`
		} `json:"instance"`
	} `json:"application"`
}

Working example:
https://play.golang.org/p/Kma32gWfghb

答案2

得分: 2

一个更简洁的解决方案是使用自定义的解组器(unmarshaler):

type Instances []Instance

func (i *Instances) UnmarshalJSON(in []byte) error {
   if len(in) > 0 && in[0] == '[' {
     var a []Instance
     if err := json.Unmarshal(in, &a); err != nil {
        return err
     }
     *i = a
     return nil
  }
  var s Instance
  if err := json.Unmarshal(in, &s); err != nil {
     return err
  }
  *i = []Instance{s}
  return nil
}

这将把一个对象解组成一个长度为1的切片。

@mkopriva 提供了一个更紧凑的解决方案:

func (i *Instances) UnmarshalJSON(in []byte) error {
	if len(in) > 0 && in[0] == '[' {
		return json.Unmarshal(in, (*[]Instance)(i))
	}

	*i = Instances{{}}
	return json.Unmarshal(in, &(*i)[0])
}
英文:

A cleaner solution would be a custom unmarshaler:

type Instances []Instance

func (i *Instances) UnmarshalJSON(in []byte) error {
   if len(in)>0 && in[0]=='[' {
     var a []Instance
     if err:=json.Unmarshal(in,&a); err!=nil {
        return err
     }
     *i=a
     return nil
  }
  var s Instance
  if err:=json.Unmarshal(in,&s) ; err!=nil {
     return err
  }
  *i=[]Instance{s}
  return nil
}

This would unmarshal an object into a slice of 1.

A more compact solution is provided by @mkopriva:

func (i *Instances) UnmarshalJSON(in []byte) error {
	if len(in) > 0 && in[0] == '[' {
		return json.Unmarshal(in, (*[]Instance)(i))
	}

	*i = Instances{{}}
	return json.Unmarshal(in, &(*i)[0])
}

huangapple
  • 本文由 发表于 2021年8月11日 11:01:56
  • 转载请务必保留本文链接:https://go.coder-hub.com/68735493.html
匿名

发表评论

匿名网友

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

确定