json.Marshal set null - PullRequest
       15

json.Marshal set null

0 голосов
/ 11 марта 2019

Я новичок в Go.Я пытаюсь сделать скребок для данных с одного моего веб-сайта, чтобы иметь возможность использовать его в приложении 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)
}

1 Ответ

3 голосов
/ 11 марта 2019

Используемые здесь абстракции кажутся мне непоследовательными. Я бы просто сделал основной вызов 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

Надеюсь, это поможет. Это становится очевидным, только если вы сделали это несколько раз, и даже не всегда: -)

...