Parsing an array of JSON

Hi there,

Currently I'm developing an app - while learning Swift and SwiftUI - that shows stock prices in real time. I'm using Alamofire to stream the data from the servers but I'm receiving this weird format:

data: [{"symbol":"SPY","price":400.9200,"size":500,"time":1617309355738,"seq":9057}]

I'm a beginner but I think this is not proper JSON, I mean, I've tried to Decode this data with the following structs:

struct Response: Codable {
  var data: Data
}

struct Data: Codable {
  var symbol: String
}

But that gives me an error. I think the problem is the data : before the array.
If you want more information, this is the exact error:

responseSerializationFailed(reason: Alamofire.AFError.ResponseSerializationFailureReason.decodingFailed(error: Swift.DecodingError.dataCorrupted(Swift.DecodingError.Context(codingPath: [], debugDescription: "The given data was not valid JSON.", underlyingError: Optional(Error Domain=NSCocoaErrorDomain Code=3840 "Invalid value around character 0." UserInfo={NSDebugDescription=Invalid value around character 0.})))))

And this is my streamRequest:

AF.streamRequest(urlStream).responseStreamDecodable(of: Response.self) { stream in
            switch stream.event {
            case let .stream(result):
                switch result {
                case let .success(value):
                    print(value)
                case let .failure(error):
                    print(error)
                }
            case let .complete(completion):
                print(completion)
            }
        }

So, the question is: Am I doing something wrong? If not, How can I decode this format?
Thank you.

It is, data should be an array of Data though:

struct Response: Codable {
  var data: [Data]
}

struct Data: Codable {
  var symbol: String
}
Decoding
let json = """
  {
    "data": [{
      "symbol": "SPY",
      "price": 400.9200,
      "size": 500,
      "time": 1617309355738,
      "seq": 9057
    }]
  }
  """
 
let decoder = JSONDecoder()
if let data = json.data(using: .utf8),
   let result = try? decoder.decode(Response.self, from: data) {
  print(result)
}

// prints: Response(data: [Data(symbol: "SPY")])
Encoding
let value = Response(data: [Data(symbol: "Swift")])

let encoder = JSONEncoder()
if let data = try? encoder.encode(value),
   let result = String(bytes: data, encoding: .utf8) {
  print(result)
}

// prints: {"data":[{"symbol":"Swift"}]}

Thanks for your quick reply. Maybe I misunderstood something but I'm not receiving data inside curly brackets as if it was a JSON response, this is my problem. The exact response I'm receiving from the server is:

data:[{"symbol":"BTCUSD","primaryExchange":"Gemini","sector":"cryptocurrency","calculationPrice":"realtime","high":null,"low":null,"latestPrice":"59187.42","latestSource":"Real time price","latestUpdate":1617370913090,"latestVolume":"0.00828046","previousClose":null}]

As you can see, before data: there is no {, the same at the end of the response.

You either need to manually remove data: and decode the string as [Data] or add { and } and decode it as Response. I never used Alamofire, but from the documentation of responseStreamDecodable(of:on:using:preprocessor:stream:) it looks like you can add a preprocessor that transforms the data just before decoding it.

I moved the topic to the Alamofire section, since it seem to be related to that framework.

1 Like

responseStream is not what you'd use here, as it's just a standard JSON response, so responseDecodable is what you want.

@denniscmartin You can use a DataPreprocessor to add the curly braces you need or remove the data:.

struct Fixer: DataPreprocessor {
    func preprocess(_ data: Data) throws -> Data {
         data.dropFirst(6) // Or 5 if there's no newline.
    }
}

Then you can just parse an [Data].

I'd also suggest renaming Data to whatever your objects really are.

2 Likes

Thank you! Your answers are really helpful. I was able to solve the issue with your explanations.

1 Like