如何解析复杂的 JSON 并在 CollectionView 内部和外部显示它
Posted
技术标签:
【中文标题】如何解析复杂的 JSON 并在 CollectionView 内部和外部显示它【英文标题】:how to parse complex JSON and show it inside and outside of CollectionView 【发布时间】:2017-01-09 09:56:13 【问题描述】:我已经使用 JSON 一个月了,一天比一天好。这是我一直在盘旋的复杂拼图。 JSON 我得到的回报是这样的:
"main_content": [
"product_title": "product 3",
"description": "",
"regular_price": "320",
"sale_price": "",
"product_currency": "$",
"size": [
"size_data": "L"
,
"size_data": "S"
],
"color": [
"color_data": "RED"
,
"color_data": "WHITE"
],
"gallery": [
"guid": "http://xxx/wp-content/uploads/2016/11/catagory1.jpg"
,
"guid": "http://xxx/wp-content/uploads/2016/11/catagory3.jpg"
]
]
现在,这里是 product_title、description、regular_price、sale_price 和 product_currency如您所见,将在数组之外。对于 size 和 color 我需要在 CollectionView 之外获取数组,我不知道如何遍历 之外的数组collectionView 或 tableView,因为我有一个 indexpath 进行迭代,但我不知道在 collectionView 或 之外做什么>tableView。最后,我的图像滑块将在 CollectionView 内,因此 gallery 项目需要在其中。这是我各自页面的视觉图像。
现在这是在我的视图
中使用Alamofire的POST调用import Foundation
import Alamofire
import SwiftyJSON
//Error could happen for these reason
enum ProductDetailsManagerError: Error
case network(error: Error)
case apiProvidedError(reason: String)
case authCouldNot(reason: String)
case authLost(reason: String)
case objectSerialization(reason: String)
// APIManager Class
class ProductDetailsManager
// THE RIGHT WAY A.K.A. "THE ONE LINE SINGLETON (NOW WITH PROOF!")
static let sharedInstance = ProductDetailsManager()
func printPublicGists(parameter: [String:Any]? , completionHandler: @escaping (Result<[ProductDetailsJSON]>) -> Void)
let url: String = "http://xxx/wp-api/products/get_product_informations/"
Alamofire.request(url, method: .post, parameters: parameter, encoding: URLEncoding.default, headers: nil)
.responseJSON (response) in
guard response.result.error == nil else
print(response.result.error!)
return
guard let value = response.result.value else
print("no string received in response when swapping oauth code for token")
return
print(value)
func fetchPublicGists(parameter: [String:Any]? , completionHandler: @escaping (Result<[ProductDetailsJSON]>) -> Void)
let url: String = "http://xxx/wp-api/products/get_product_informations/"
Alamofire.request(url, method: .post, parameters: parameter, encoding: URLEncoding.default, headers: nil)
.responseJSON response in
let result = self.gistArrayFromResponse(response: response)
completionHandler(result)
// Download Image from URL
func imageFrom(urlString: String, completionHandler: @escaping (UIImage?, Error?) -> Void)
let _ = Alamofire.request(urlString)
.response dataResponse in
// use the generic response serializer that returns Data
guard let data = dataResponse.data else
completionHandler(nil, dataResponse.error)
return
let image = UIImage(data: data)
completionHandler(image, nil)
//gistArrayFromResponse function
private func gistArrayFromResponse(response: DataResponse<Any>) -> Result<[ProductDetailsJSON]>
// For Network Error
guard response.result.error == nil else
print(response.result.error!)
return .failure(RueDu8APIManagerError.network(error: response.result.error!))
// JSON Serialization Error, make sure we got JSON and it's an array
guard let jsonArray = response.result.value else
print("did not get array of homeFeed object as JSON from API")
return .failure(RueDu8APIManagerError.objectSerialization(reason: "Did not get JSON dictionary in response"))
//turn JSON into gists
//let gistss = jsonArray.flatMap HomeFeedJSON(items: $0)
var gists = [ProductDetailsJSON]()
let jsonR = JSON(jsonArray)
let main_content = jsonR["main_content"].array
for item in main_content!
gists.append(ProductDetailsJSON(items: item))
return .success(gists)
//gistArrayFromResponse() function ends here
这是我的模型类
import Foundation
import SwiftyJSON
class ProductDetailsJSON
var _product_title: String?
var _description: String?
var _regular_price: String?
var _sale_price: String?
var _product_currency: String?
var _size: String?
var _color: String?
var _image: URL?
init(items: JSON)
self._product_title = items["product_title"].stringValue
self._description = items["description"].stringValue
self._regular_price = items["regular_price"].stringValue
self._sale_price = items["sale_price"].stringValue
self._product_currency = items["product_currency"].stringValue
let sizeData = items["size"].arrayValue
for itemsIMG in sizeData
self._size = itemsIMG["size_data"].stringValue
let colorData = items["color"].arrayValue
for itemsColor in colorData
self._size = itemsColor["color_data"].stringValue
let galleryImg = items["gallery"].arrayValue
for image in galleryImg
self._image = image["guid"].URL
var product_title: String
if _product_title == nil
_product_title = ""
return _product_title!
var description: String
if _description == nil
_description = ""
return _description!
var regular_price: String
if _regular_price == nil
_regular_price = ""
return _regular_price!
var sale_price: String
if _sale_price == nil
_sale_price = ""
return _sale_price!
var product_currency: String
if _product_currency == nil
_product_currency = ""
return _product_currency!
var product_color: String
if _color == nil
_color = ""
return _size!
var product_image: URL
if _image == nil
let myURL = "http://www.clker.com/cliparts/d/L/P/X/z/i/no-image-icon-hi.png"
let noImage: URL = URL(string: myURL)!
_image = noImage
return _image!
这是我的 controller 类,我正在努力展示 size 、 color 和 gallery 项目来自 JSON
import UIKit
import DropDown
import Alamofire
import SwiftyJSON
class ShopItemVC: UIViewController , UICollectionViewDataSource, UICollectionViewDelegate
@IBOutlet weak var collectionView: UICollectionView!
@IBOutlet weak var scrollView: UIScrollView!
@IBOutlet weak var contentView: UIView!
@IBOutlet weak var selectedProductImg: UIImageView!
@IBOutlet weak var backgroundCardView1: UIView!
@IBOutlet weak var backgroundCardView2: UIView!
@IBOutlet weak var backgroundCardView3: UIView!
@IBOutlet weak var sizeBtn: NiceButton!
@IBOutlet weak var colorBtn: NiceButton!
@IBOutlet weak var productPrice: UILabel!
@IBOutlet weak var productTitle: UILabel!
// var Title = [ProductDetailsJSON]()
var product_id:Int? //got value from SpecificCatagoryVC
var product_detail = [ProductDetailsJSON]()
var reloadData = 0
let sizeDropDown = DropDown()
let colorDropDown = DropDown()
lazy var dropDowns: [DropDown] =
return [
self.sizeDropDown,
self.colorDropDown
]
()
let CatagoryPic = ["catagory1","catagory2","catagory3","catagory4","catagory5","catagory6","c atagory7"]
// let CatagoryPicture = [ProductDetailsJSON]()
override func viewDidLoad()
super.viewDidLoad()
// Do any additional setup after loading the view.
sizeBtn.layer.borderWidth = 1.2
sizeBtn.layer.borderColor = UIColor.black.cgColor
colorBtn.layer.borderWidth = 1.2
colorBtn.layer.borderColor = UIColor.black.cgColor
backgroundCardView1.backgroundColor = UIColor.white
backgroundCardView1.layer.cornerRadius = 5.0
backgroundCardView1.layer.masksToBounds = false
backgroundCardView1.layer.shadowColor = UIColor.black.withAlphaComponent(0.2).cgColor
backgroundCardView1.layer.shadowOffset = CGSize(width: 0, height: 0)
backgroundCardView1.layer.shadowOpacity = 0.8
backgroundCardView2.backgroundColor = UIColor.white
backgroundCardView2.layer.cornerRadius = 5.0
backgroundCardView2.layer.masksToBounds = false
backgroundCardView2.layer.shadowColor = UIColor.black.withAlphaComponent(0.2).cgColor
backgroundCardView2.layer.shadowOffset = CGSize(width: 0, height: 0)
backgroundCardView2.layer.shadowOpacity = 0.8
backgroundCardView3.backgroundColor = UIColor.white
backgroundCardView3.layer.cornerRadius = 5.0
backgroundCardView3.layer.masksToBounds = false
backgroundCardView3.layer.shadowColor = UIColor.black.withAlphaComponent(0.2).cgColor
backgroundCardView3.layer.shadowOffset = CGSize(width: 0, height: 0)
backgroundCardView3.layer.shadowOpacity = 0.8
setupDropDowns()
override func viewDidAppear(_ animated: Bool)
self.scrollView.contentSize = CGSize(width: self.view.frame.width, height: self.view.frame.height + 40)
loadGists(parameter: ["product_id":product_id ?? 0])
func setupDropDowns()
setupSizeDropDown()
setupColorDropDown()
func setupSizeDropDown()
sizeDropDown.anchorView = sizeBtn
sizeDropDown.bottomOffset = CGPoint(x: 0, y: sizeBtn.bounds.height)
// You can also use localizationKeysDataSource instead. Check the docs.
sizeDropDown.dataSource = [
"XXL",
"XL",
"L",
"M",
"S"
]
// Action triggered on selection
sizeDropDown.selectionAction = [unowned self] (index, item) in
self.sizeBtn.setTitle(item, for: .normal)
print(item)
func setupColorDropDown()
colorDropDown.anchorView = colorBtn
colorDropDown.bottomOffset = CGPoint(x: 0, y: colorBtn.bounds.height)
// You can also use localizationKeysDataSource instead. Check the docs.
colorDropDown.dataSource = [
"Red",
"Blue",
"White",
"Purple",
"Pink"
]
// Action triggered on selection
colorDropDown.selectionAction = [unowned self] (index, item) in
self.colorBtn.setTitle(item, for: .normal)
print(item)
override func didReceiveMemoryWarning()
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
func loadGists(parameter: [String:Any]?)
ProductDetailsManager.sharedInstance.fetchPublicGists(parameter: parameter)
(result) in
guard result.error == nil else
self.handleLoadGistsError(result.error!)
return
if let fetchedGists = result.value
self.product_detail = fetchedGists
self.reloadData = 1
self.collectionView?.reloadData()
func handleLoadGistsError(_ error: Error)
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int
return CatagoryPic.count
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "ShopItemCell", for: indexPath) as! ShopItemCell
if reloadData == 1
let myProduct = self.product_detail[indexPath.row]
self.productTitle.text = myProduct.product_title
cell.shopItemPic.image = UIImage(named: CatagoryPic[(indexPath as NSIndexPath).row])
return cell
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath)
// here you know which item is selected by accessing indexPath.item property, for example:
let selectedImage = UIImage(named: CatagoryPic[(indexPath as NSIndexPath).item])
selectedProductImg.image = selectedImage
@IBAction func sizeBtnPressed(_ sender: Any)
sizeDropDown.show()
//print("size btn pressed")
@IBAction func colorBtnPressed(_ sender: Any)
colorDropDown.show()
@IBAction func backBtn(_ sender: AnyObject)
self.dismiss(animated: true, completion: nil)
我在其他一些班级也面临同样的问题。希望如果我得到这个解决方案,我将能够解决这些问题。提前致谢。
【问题讨论】:
【参考方案1】:首先要注意的是,您在product_color
变量中返回的是大小值而不是颜色。
此外,当您遍历 JSON 中的数组时,您将变量设置为仅是最终值。例如这里:
let sizeData = items["size"].arrayValue
for itemsIMG in sizeData
self._size = itemsIMG["size_data"].stringValue
JSON 是
"size": [
"size_data": "L"
,
"size_data": "S"
]
所以_size
将被设置为"S"
,而"L"
将永远不会被分配。我建议将 _size
、_color
和 _image
更改为
var _sizes: [String] = []
var _colors: [String] = []
var _images: [String] = []
然后在遍历 JSON 数组时:
let sizeData = items["size"].arrayValue
for itemsIMG in sizeData
let size = itemsIMG["size_data"].stringValue
_sizes.append(size)
如果我的理解正确,那么您希望在收到带有要点的响应时更新您的下拉数据。
sizeDropDown.dataSource = product_detail.product_sizes
其他下拉菜单也是如此。
并在类似sale_price
和regular_price
的模型中创建product_sizes、product_colors和product_images变量。
【讨论】:
帮了我很多,但我仍然不是重点。事情是我想保留 MVC 设计模式,因此我使用的是模型、视图和控制器。如果我不使用它,我可以在我的 ViewController 中获取 json 并且一切都会好起来的,但我想保留设计模式。在这里你可以看到我有 append 数组作为 let myProduct = self.product_detail[indexPath.row] 。这是我可以将数组附加到 cellForItemAt indexPath 内的地方,但是如何将数组附加到它之外?我很困惑。以上是关于如何解析复杂的 JSON 并在 CollectionView 内部和外部显示它的主要内容,如果未能解决你的问题,请参考以下文章
如何解析 JSON 响应并在 Objective C 中使用它?
如何从本地 JSON 文件解析数据并保存在模型类中并在 tableview 中使用
如何使用 Klaxon 解析嵌套 JSON 并在 recyclerview 中显示?