如何解析复杂的 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_titledescriptionregular_pricesale_priceproduct_currency如您所见,将在数组之外。对于 sizecolor 我需要在 CollectionView 之外获取数组,我不知道如何遍历 之外的数组collectionViewtableView,因为我有一个 indexpath 进行迭代,但我不知道在 collectionView 之外做什么>tableView。最后,我的图像滑块将在 CollectionView 内,因此 gallery 项目需要在其中。这是我各自页面的视觉图像。

现在这是在我的视图

中使用AlamofirePOST调用
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 类,我正在努力展示 sizecolorgallery 项目来自 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_priceregular_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 中显示?

scala spray-json 如何解析一个复杂的 数组JSON

如何解析 JSON 对象并在表格中显示? [关闭]

解析复杂JSON数据