json.Marshal 设为空

json.Marshal set null

我是围棋新手。我正在尝试从我的一个网站上抓取数据,以便能够在 Go 应用程序中使用它。

我使用 goroutines 和 sync.WaitGroup 等待结果,但我遇到了问题。如果我使用 goroutines 并尝试对我的数据集使用 json.Marshal,我在结构中有空数组,即填充在 goroutines 中。

如果我在没有例程的情况下填充我的结构,那么我的一切都运行良好。

这是我的结构:

type CategoryScrapper struct {
    Name        string                `json:"name"`
    Link        string                `json:"link"`
    Products []Product.ProductData `json:"products"`
}

type ProductData struct {
    Name        string `json:"name"`
    Link        string `json:"link"`
    Thumbnail   string `json:"thumbnail"`
    OriginPrice string `json:"OriginPrice"`
    Excerpt     string `json:"Excerpt"`
}

这是我的应用程序的一部分

func main() {
    cs := Category.CategoryScrapper{
        Name: "Name",
        Link: "/link",
    }

    wg := new(sync.WaitGroup)
    go cs.GetProducts(wg)
    wg.Wait()

    res, _ := json.Marshal(cs)

    fmt.Println(string(res))
}

func (s *CategoryScrapper) GetProducts(pool *sync.WaitGroup) {
    pool.Add(1)
    defer pool.Done()

    maxPageNum := s.getMaxPageNum()
    localPool := new(sync.WaitGroup)

    s.Products = make([]Product.ProductData, 0)

    for i := 1; i <= maxPageNum; i++ {
        go s.getPage(i, localPool)
    }

    localPool.Wait()
}

func (s *CategoryScrapper) getPage(page int, waitingPool *sync.WaitGroup) {
    product := Product.ProductData{
        Name: "Name",
        Link: "Link",
        Thumbnail: "Thumb",
        OriginPrice: "1111",
        Excerpt: "Excerpt",
    }

    s.Products = append(s.Products, product)
}

这里使用的抽象对我来说似乎不一致。我只是将对 GetProducts 的基本调用设为阻塞调用,然后在需要时使用等待组。简化后,您可以看到在写入数据的地方没有发生同步操作。 (在 getPage 中)所以你最终得到的是 nil

竞争条件已修复

package main

import (
    "encoding/json"
    "fmt"
    "sync"
)

type CategoryScrapper struct {
    Name     string        `json:"name"`
    Link     string        `json:"link"`
    Products []ProductData `json:"products"`
}

type ProductData struct {
    Name        string `json:"name"`
    Link        string `json:"link"`
    Thumbnail   string `json:"thumbnail"`
    OriginPrice string `json:"OriginPrice"`
    Excerpt     string `json:"Excerpt"`
}

func (*CategoryScrapper) getMaxPageNum() int {
    return 1
}
func main() {
    cs := CategoryScrapper{
        Name: "Name",
        Link: "/link",
    }

    cs.GetProducts()

    res, err := json.Marshal(cs)
    if err != nil {
        fmt.Printf("ERROR: %v", err)
    }

    fmt.Println(string(res))
}

func (s *CategoryScrapper) GetProducts() {
    maxPageNum := s.getMaxPageNum()
    var wg sync.WaitGroup
    ch := make(chan ProductData)
    go func() {
        for p := range ch {
            s.Products = append(s.Products, p)
        }
        wg.Done()
    }()

    for i := 1; i <= maxPageNum; i++ {
        wg.Add(1)
        go func(i int) {
            defer wg.Done()
            ch <- s.getPage(i)
        }(i)
    }

    wg.Wait()
    // make sure we close the chan reader go routine
    wg.Add(1)
    close(ch)
    wg.Wait()
}

func (s *CategoryScrapper) getPage(page int) ProductData {
    return ProductData{
        Name:        "Name",
        Link:        "Link",
        Thumbnail:   "Thumb",
        OriginPrice: "1111",
        Excerpt:     "Excerpt",
    }
}

几个文体要点: 对于您的 Products 切片,您不需要将其初始化为零长度大小,nil 可以追加到。 我删除了一个数字包前缀,例如 Product 如果您可以轻松地在 play.golang.org 中找到 运行 的示例,它可以帮助人们回答

希望这对您有所帮助。只有当你做了几次之后才会明显,即使那样,也不总是:-)