解码为映射类型

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

Unmarshal into map

问题

如何将数据解组成一个映射(map)?

JSON 是一个对象列表,我想在解组过程中将其转换为映射。然而,初始化映射似乎会创建一个未正确返回的新对象。

type foo struct {
    Id   string `json:"id"`
    Name string `json:"name"`
}

type fooList []*foo
type fooMap map[string]*foo

func (f fooMap) UnmarshalJSON(data []byte) error {
    f := fooMap{} // 因为映射未初始化,所以需要这样做

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

    for i := 0; i < len(results); i++ {
        result := results[i]
        f[result.Id] = result
    }

    return nil
}

我已经检查过确保没有错误,并且在 Unmarshal 函数结束时映射包含了正确的值,但是当我查看应该解组到的对象时,它是空的。下面是我如何解组包含 fooMaps 的结构体切片的父对象。

type bar struct {
    Foos fooMap `json:"foos"`
}

type bars []*bar

...

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

// 这里 results 包含一个 bar 项(正确的),但是 Foos 属性根本不存在

我还尝试将 Foos 更改为 *fooMap 类型,并将解组函数更新为 func (f *fooMap) UnmarshalJSON(...),但是解组函数从未被调用。

英文:

How do you unmarshal data into a map?

The JSON is a list of objects and I want to convert this into a map during the unmarshaling. However, it appears that initializing the map creates a new object that isn't returned correctly.

type foo struct {
    Id string `json:&quot;id&quot;`
    Name string `json:&quot;name&quot;`
}

type fooList []*foo
type fooMap map[string]*foo

func (f fooMap) UnmarshalJSON(data []byte) error {
    f := fooMap{} // required since map is not initialized

    var results fooList
    if err := json.Unmarshal(data, &amp;results); err != nil {
        return err
    }

    for i := 0; i &lt; len(results); i++ {
        result := results[i]
        f[result.Id] = result
    }

    return nil
}

I've checked to make sure there are no errors and that the map contains the correct values at the end of the Unmarshal function, but when I look at the object that the results should have been unmarshalled into, it is empty. Here is how I'm unmarshaling the parent object which is a slice of structs that contain fooMaps.

type bar struct {
    Foos fooMap `json:&quot;foos&quot;`
}

type bars []*bar

...

var results bars
if err := json.Unmarshal(data, &amp;results); err != nil {
    return err
}

// here results contains a single bar item (which is correct) but the Foos
// property does not exist at all

I also tried changing Foos to be of type *fooMap and then updated the unmarshal function to be func (f *fooMap) UnmarshalJSON(...) but then the unmarshal function is never called.

答案1

得分: 3

这似乎非常复杂,有几个地方让人感到困惑。

  • 在UnmarshalJSON过程中,你真的需要中间的fooList吗?这会使JSON表示与你定义的类型不同,这确实让我一开始感到困惑。
  • 至少在这里,没有理由为每个东西定义自己的类型,这使得代码非常难以阅读,因为它隐藏了结构在另一层间接性之后。

所以你的问题的关键是fooMap方法没有指针接收器。这使得在方法内部无法将非nil的map赋值给接收器。你确实需要一个指针接收器,在每个索引操作上需要对其进行解引用。在你的代码中,你只是将map数据结构赋值给接收器的临时副本(我可能还要补充一句,这种方式是非有效的)。

func (f *fooMap) UnmarshalJSON(data []byte) error {
    *f = make(map[string]*foo) // 需要初始化map
    
    var results fooList
    if err := json.Unmarshal(data, &results); err != nil {
        return err
    }
    
    for i := 0; i < len(results); i++ {
        result := results[i]
        (*f)[result.Id] = result
    }
    
    return nil
}

这是我对你的JSON的最佳猜测:http://play.golang.org/p/WbwZJxphTj

英文:

This seems very over-complicated, and there's a couple confusing parts here.

  • Do you really need the intermediary fooList during UnmarshalJSON? This makes the json representation look different than the types you define, which definitely confused me at first.
  • At least here, there's no reason to be defining your own types for everything, and it's making the code very hard to read, as it hides the structure behind another layer of indirection.

So the crux of your problem is that the fooMap method does not have a pointer receiver. This makes it impossible to assign a non-nil map to the receiver from within your method. You do need a pointer receiver, which you need to dereference on each index operation. In your code you're simply assigning the map data structure to a temporary copy of the receiver (in a non-valid way I might add).

func (f *fooMap) UnmarshalJSON(data []byte) error {
	*f = make(map[string]*foo) // required since map is not initialized

	var results fooList
	if err := json.Unmarshal(data, &amp;results); err != nil {
		return err
	}

	for i := 0; i &lt; len(results); i++ {
		result := results[i]
		(*f)[result.Id] = result
	}

	return nil
}

And with my best guess of your json: http://play.golang.org/p/WbwZJxphTj

huangapple
  • 本文由 发表于 2014年4月3日 01:00:11
  • 转载请务必保留本文链接:https://go.coder-hub.com/22818208.html
匿名

发表评论

匿名网友

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

确定