decode of JSON array into swift Array

HI All,


I have litterally been trying this for hours and I cannot get it to work! I am hopefull someone can assist. Appologies its a little messy as I have been messing around with it so much. I have an array in JSON, which I want to put into an array in Swift. It normally comes from a server, but I have tested it using a string to try and find the error. I did get it to work with a single JSON object earlier, but now there is an array, no luck.


I don't get any errors, the program runs, but there is no data in tableData from the decoder. The rowcount is 0 and the output of print(tableData) is Optional(WellandPowerLive.StockByWarehouseResponse(StockByWarehouse: []))


If its simpler to achieve, I could drop the top level, as i am writing the server side also, if the JSON was just like this does it make it easier?:


JSONString = "[

{\"ItemCode\":\"Test\",\"OnHand\":3,\"IsCommited\":5,\"OnOrder\":5},

{\"ItemCode\":\"Test2\",\"OnHand\":3,\"IsCommited\":2,\"OnOrder\":1}

]"


here is the current code. TIA for all assistance.


struct StockByWarehouseResponse: Codable {

let StockByWarehouse: [SAPB1_StockItem] = Array()

}

struct SAPB1_StockItem: Codable {

let WhsCode: String

let ItemCode: String

let OnHand: Decimal

let IsCommited: Decimal

let OnOrder: Decimal

}

JSONString = "{\"StockByWarehouse\":[{\"ItemCode\":\"Test\",\"OnHand\":3,\"IsCommited\":5,\"OnOrder\":5}, {\"ItemCode\":\"Test2\",\"OnHand\":3,\"IsCommited\":2,\"OnOrder\":1}}"

print(JSONString)

let jsonData = JSONString.data(using: .utf8)!

let decoder = JSONDecoder()

tableData = try! decoder.decode(StockByWarehouseResponse.self, from: jsonData)

print(tableData)

print("Rows in array: \(tableData?.StockByWarehouse.count)")

Accepted Reply

You have multiple things wrong here, but as you say some of that just comes from trying things.


I think the main problem is that you've declared the "StockByWarehouse" property with 'let', not 'var', which means the decoded array cannot be assigned to it. This is arguable a bug in Decodable conformance, I suppose, since the decoding code probably shouldn't be synthesized if it won't work. Also, you didn't supply values for the "WhsCode" property.


Here's a slightly revised version that works in a playground:


let JSONString = """
  {"StockByWarehouse":
  [
  {"WhsCode":"A","ItemCode":"Test","OnHand":3,"IsCommited":5,"OnOrder":5},
  {"WhsCode":"B","ItemCode":"Test2","OnHand":3,"IsCommited":2,"OnOrder":1}
  ]
  }
  """
print(JSONString)
let jsonData = JSONString.data(using: .utf8)!
do {
  let decoder = JSONDecoder()
  let tableData = try decoder.decode(StockByWarehouseResponse.self, from: jsonData)
  print(tableData)
  print("Rows in array: \(tableData.StockByWarehouse.count)")
}
catch {
  print (error)
}


It makes things a bit easier if you use a multi-line literal for the string, so you don't have to escape everything. Catching errors is also a good idea, because the error message will tell you where in the decoding it failed.


>> If its simpler to achieve, I could drop the top level


It's up to you. If you do, you would replace the line that does the decoding with this:


let tableData = try decoder.decode([SAPB1_StockItem].self, from: jsonData)


That decodes an array at the top level.

Replies

You have multiple things wrong here, but as you say some of that just comes from trying things.


I think the main problem is that you've declared the "StockByWarehouse" property with 'let', not 'var', which means the decoded array cannot be assigned to it. This is arguable a bug in Decodable conformance, I suppose, since the decoding code probably shouldn't be synthesized if it won't work. Also, you didn't supply values for the "WhsCode" property.


Here's a slightly revised version that works in a playground:


let JSONString = """
  {"StockByWarehouse":
  [
  {"WhsCode":"A","ItemCode":"Test","OnHand":3,"IsCommited":5,"OnOrder":5},
  {"WhsCode":"B","ItemCode":"Test2","OnHand":3,"IsCommited":2,"OnOrder":1}
  ]
  }
  """
print(JSONString)
let jsonData = JSONString.data(using: .utf8)!
do {
  let decoder = JSONDecoder()
  let tableData = try decoder.decode(StockByWarehouseResponse.self, from: jsonData)
  print(tableData)
  print("Rows in array: \(tableData.StockByWarehouse.count)")
}
catch {
  print (error)
}


It makes things a bit easier if you use a multi-line literal for the string, so you don't have to escape everything. Catching errors is also a good idea, because the error message will tell you where in the decoding it failed.


>> If its simpler to achieve, I could drop the top level


It's up to you. If you do, you would replace the line that does the decoding with this:


let tableData = try decoder.decode([SAPB1_StockItem].self, from: jsonData)


That decodes an array at the top level.