英文:
I have several JSON's of the same structure. They have object that is an array of objects . How to I append these arrays into one array?
问题
我想做的事情:
我想向以下URL发送多个GET请求:
https://catalog.wb.ru/brands/m/catalog?page=1&limit=300&brand=5786&dest=-1257786&sort=pricedown
然后收集“product”对象内的所有数据。键“PAGE”的值是自动递增的,以获取所有页面的数据。
实际上,我不太确定是否真的需要构建一个JSON对象并将其发送到前端。也许在for循环中收到新的响应时,逐个发送不同的请求会更好?
我所做的:
创建了requestBodyBytes []byte
和ProductsBytes []byte
,以便将它们与ioutil.ReadAll
的[]bytes
附加在一起。
打印requestBodyBytes
的长度,我发现它随着每个请求的增加而扩展,但在我解组后,输出中看到空的结构体。
我明白这是因为每个单独的请求我都会得到type Response
的新JSON。但是,如果我需要一个由多个type Response
的JSON组成的“product”对象的切片呢?
注意:需要在for循环中初始化requestBodyBytes
,以便在页面上没有信息时使用它停止发送请求,因为服务器会返回200代码和空JSON。
提前谢谢!
const URL = "https://catalog.wb.ru/brands/m/catalog?page=%d&limit=300&brand=5786&dest=-1257786&sort=pricedown"
type Response struct {
Data struct {
Products []Product `json:"products"`
} `json:"data"`
}
type Product struct {
ID int `json:"id"`
Name string `json:"name"`
Price int `json:"priceU"`
Rating float32 `json:"reviewRating"`
Sale int `json:"sale"`
New bool `json:"isNew"`
}
func main() {
var response Response
var products Response //也尝试将其定义为[]Response
var ProductsBytes []byte
for i := 1; ; i++ {
resp, err := http.Get(fmt.Sprintf(URL, i))
if err != nil {
fmt.Printf("#1 Error: %s", err)
}
defer resp.Body.Close()
bytes, err := ioutil.ReadAll(resp.Body)
var requestBodyBytes []byte
requestBodyBytes = append(requestBodyBytes, bytes...)
ProductsBytes = append(ProductsBytes, bytes...)
json.Unmarshal(requestBodyBytes, &response)
fmt.Println(resp.Status)
fmt.Printf("\n来自第%d页的切片\n字节长度:%d\n", i, len(bytes))
fmt.Printf("finalResult的长度:%d\n", len(requestBodyBytes))
if len(response.Data.Products) == 0 {
fmt.Println("没有更多数据")
break
}
}
json.Unmarshal(ProductsBytes, &products)
fmt.Println(response)
fmt.Println(products)
fmt.Println(len(products))
}
英文:
What I want to do:
I want to send several GET-requests to this URL:
https://catalog.wb.ru/brands/m/catalog?page=1&limit=300&brand=5786&dest=-1257786&sort=pricedown
Then gather all the data inside of a "product" object. The value of key "PAGE" is autoincrementing to get data from all pages.
Actually I'm not quite sure that I really need to make up one JSON to send it to front-end. Maybe it's better to send different requests as I get new responses in a for loop?
What I did:
Made correct structures. With single request everything is working fine.
Created requestBodyBytes []byte
and ProductsBytes []byte
in order to append them with []bytes
from ioutil.ReadAll
.
Printing length of requestBodyBytes
I see that it expands with every request but after I Unmarshal it I see empty struct in the output.
I understand that it happens because every single request I get new JSON of type Response
. But what if I need a slice of Product structs
composed of "product" objects from several JSON's of type Response
?
Note: needed to init requestBodyBytes
inside of for loop to use it to stop sending requests because the server gives 200 code and empty JSON when there's no information on a page.
Thank you in advance!
const URL = "https://catalog.wb.ru/brands/m/catalog?page=%d&limit=300&brand=5786&dest=-1257786&sort=pricedown"
type Response struct {
Data struct {
Products []Product `json:"products"`
} `json:"data"`
}
type Product struct {
ID int `json:"id"`
Name string `json:"name"`
Price int `json:"priceU"`
Rating float32 `json:"reviewRating"`
Sale int `json:"sale"`
New bool `json:"isNew"`
}
func main() {
var response Response
var products Response //Also tried to make it []Response
var ProductsBytes []byte
for i := 1; ; i++ {
resp, err := http.Get(fmt.Sprintf(URL, i))
if err != nil {
fmt.Printf("#1 Error: %s", err)
}
defer resp.Body.Close()
bytes, err := ioutil.ReadAll(resp.Body)
var requestBodyBytes []byte
requestBodyBytes = append(requestBodyBytes, bytes...)
ProductsBytes = append(ProductsBytes, bytes...)
json.Unmarshal(requestBodyBytes, &response)
fmt.Println(resp.Status)
fmt.Printf("\nSlice from page #%d\nLength of bytes: %d\n", i, len(bytes))
fmt.Printf("Length of finalResult: %d\n", len(requestBodyBytes))
if len(response.Data.Products) == 0 {
fmt.Println("There's no more data")
break
}
}
json.Unmarshal(ProductsBytes, &products)
fmt.Println(response)
fmt.Println(products)
fmt.Println(len(products))
}
答案1
得分: 2
没有理由收集所有原始响应字节。只需逐个解组每个响应,并将每个页面的产品追加到保存所有产品的切片中。此外,在循环中调用defer resp.Body.Close()
可能不是你想要的。延迟语句只在循环结束后执行,因此无法重用连接进行请求。将循环体提取到自己的函数中可以使代码更清晰:
package main
import (
"encoding/json"
"errors"
"fmt"
"log"
"net/http"
)
const URL = "https://catalog.wb.ru/brands/m/catalog?page=%d&limit=300&brand=5786&dest=-1257786&sort=pricedown"
type Response struct {
Data struct {
Products []Product `json:"products"`
} `json:"data"`
}
type Product struct {
ID int `json:"id"`
Name string `json:"name"`
Price int `json:"priceU"`
Rating float32 `json:"reviewRating"`
Sale int `json:"sale"`
New bool `json:"isNew"`
}
func main() {
var allProducts []Product
for i := 1; ; i++ {
page, err := fetchPage(i)
if err != nil {
log.Fatal(err) // TODO
}
allProducts = append(allProducts, page...)
if len(page) == 0 {
break
}
}
fmt.Println(allProducts)
fmt.Println(len(allProducts))
}
func fetchPage(i int) ([]Product, error) {
resp, err := http.Get(fmt.Sprintf(URL, i))
if err != nil {
return nil, err
}
defer resp.Body.Close()
if resp.StatusCode != 200 {
return nil, errors.New(resp.Status)
}
var response Response
err = json.NewDecoder(resp.Body).Decode(&response)
if err != nil {
return nil, err
}
return response.Data.Products, nil
}
英文:
There is no reason to collect all the raw response bytes. Just unmarshal each response individually and append the products for each page to some slice holding all of the products. Also, calling defer resp.Body.Close()
in a loop is probably not what you want. The deferred statement executes only after the loop finishes, so connections cannot be re-used for the requests. Extracting the loop body into its own function makes this much cleaner:
package main
import (
"encoding/json"
"errors"
"fmt"
"log"
"net/http"
)
const URL = "https://catalog.wb.ru/brands/m/catalog?page=%d&limit=300&brand=5786&dest=-1257786&sort=pricedown"
type Response struct {
Data struct {
Products []Product `json:"products"`
} `json:"data"`
}
type Product struct {
ID int `json:"id"`
Name string `json:"name"`
Price int `json:"priceU"`
Rating float32 `json:"reviewRating"`
Sale int `json:"sale"`
New bool `json:"isNew"`
}
func main() {
var allProducts []Product
for i := 1; ; i++ {
page, err := fetchPage(i)
if err != nil {
log.Fatal(err) // TODO
}
allProducts = append(allProducts, page...)
if len(page) == 0 {
break
}
}
fmt.Println(allProducts)
fmt.Println(len(allProducts))
}
func fetchPage(i int) ([]Product, error) {
resp, err := http.Get(fmt.Sprintf(URL, i))
if err != nil {
return nil, err
}
defer resp.Body.Close()
if resp.StatusCode != 200 {
return nil, errors.New(resp.Status)
}
var response Response
err = json.NewDecoder(resp.Body).Decode(&response)
if err != nil {
return nil, err
}
return response.Data.Products, nil
}
答案2
得分: 0
package main
import (
"encoding/json"
"fmt"
"io/ioutil"
"net/http"
)
const URL = "https://catalog.wb.ru/brands/m/catalog?page=%d&limit=300&brand=5786&dest=-1257786&sort=pricedown"
type Response struct {
Data struct {
Products []Product `json:"products"`
} `json:"data"`
}
type Product struct {
ID int `json:"id"`
Name string `json:"name"`
Price int `json:"priceU"`
Rating float32 `json:"reviewRating"`
Sale int `json:"sale"`
New bool `json:"isNew"`
}
func main() {
var products = []Product{}
for i := 1; ; i++ {
resp, err := http.Get(fmt.Sprintf(URL, i))
if err != nil {
fmt.Printf("#1 Error: %s", err)
}
defer resp.Body.Close()
bytes, err := ioutil.ReadAll(resp.Body)
if err != nil {
fmt.Printf("#2 Error: %s", err)
}
// 将响应解析为 Response 结构体
var response Response
err = json.Unmarshal(bytes, &response)
if err != nil {
fmt.Printf("#3 Error: %s", err)
break
}
fmt.Printf("\n第 %d 页的切片\n字节长度:%d\n", i, len(bytes))
fmt.Printf("finalResult 的长度:%d\n", len(response.Data.Products))
if len(response.Data.Products) == 0 {
fmt.Println("没有更多数据")
break
}
// 如果有产品,则将其追加到 products 切片中
products = append(products, response.Data.Products...)
}
fmt.Println("products:", products)
fmt.Println("产品总数:", len(products))
}
英文:
Instead of unmarshaling the reponse with []byte, we can unmarshal it with the Response
struct.
Here is the updated code
package main
import (
"encoding/json"
"fmt"
"io/ioutil"
"net/http"
)
const URL = "https://catalog.wb.ru/brands/m/catalog?page=%d&limit=300&brand=5786&dest=-1257786&sort=pricedown"
type Response struct {
Data struct {
Products []Product `json:"products"`
} `json:"data"`
}
type Product struct {
ID int `json:"id"`
Name string `json:"name"`
Price int `json:"priceU"`
Rating float32 `json:"reviewRating"`
Sale int `json:"sale"`
New bool `json:"isNew"`
}
func main() {
var products = []Product{}
for i := 1; ; i++ {
resp, err := http.Get(fmt.Sprintf(URL, i))
if err != nil {
fmt.Printf("#1 Error: %s", err)
}
defer resp.Body.Close()
bytes, err := ioutil.ReadAll(resp.Body)
if err != nil {
fmt.Printf("#2 Error: %s", err)
}
// The response has defined as `Response` type and unmarshaling
var response Response
err = json.Unmarshal(bytes, &response)
if err != nil {
fmt.Printf("#3 Error: %s", err)
break
}
fmt.Printf("\nSlice from page #%d\nLength of bytes: %d\n", i, len(bytes))
fmt.Printf("Length of finalResult: %d\n", len(response.Data.Products))
if len(response.Data.Products) == 0 {
fmt.Println("There's no more data")
break
}
// if there has products then append into the Products slice
products = append(products, response.Data.Products...)
}
fmt.Println("products:", products)
fmt.Println("Total products count:", len(products))
}
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论