0

I am receiving JSON text, converting it to Data, and then using JSONDecoder to create a concrete type represented by the JSON text/string.

It does work with my "complex" data structure (which implements Codable), or even a simple array of Int as shown below:

import Foundation

let jsonTextContainigArrayOfInt: String = "[1,2,3]"
let data = jsonTextContainigArrayOfInt.data(using: .utf8)!

do {
    let arrayOfInt: [Int] = try JSONDecoder().decode([Int].self, from: data)
    for n in arrayOfInt {
        print(n)
    }
}
catch {
    print(error)
}

The previous code works and correctly creates the array of Int and prints them.

The problem occurs when doing this same approach with a single Int in the JSON-text:

import Foundation

let jsonTextContainigOneInt: String = "1"
let data = jsonTextContainigOneInt.data(using: .utf8)!

do {
    let myInt: Int = try JSONDecoder().decode(Int.self, from: data)
    print(myInt)
}
catch {
    print(error)
}

For this second approach, I get the following error:

"The operation could not be completed"

*** Edit ***

Bug report for this already exists: https://bugs.swift.org/browse/SR-6163

  • 1
    "1" is not valid JSON. – picciano Apr 24 '18 at 16:09
  • Thanks for your time picciano. Could you comment on this: https://stackoverflow.com/a/7487892/8284660. It states that values such as a single string should be valid now (in RFC 7159) – User Not Found Apr 24 '18 at 16:09
  • Since (NS)JSONSerialization doesn't accept it, it wouldn't surprise at all that JSONDecoder too. See the documentation: https://developer.apple.com/documentation/foundation/nsjsonserialization?language=objc Also, it seems that its RFC 4627 compliant only, not the new one (https://github.com/gnustep/libs-base/blob/master/Source/NSJSONSerialization.m) – Larme Apr 24 '18 at 16:14
  • Use `JSONSerialization`, check this answer https://stackoverflow.com/questions/47941826/allow-fragments-with-jsondecoder?noredirect=1&lq=1 – schmidt9 Apr 24 '18 at 16:21
  • Thank you both! I did see that JSONSerialization doesn't accept non-array, non-dictionary objects as valid JSON even though (as I told @picciano), in RFC-7159, it is now valid JSON. I now see that JSONDecoder doesn't have a ".allowFragments" option as in JSONSerialization. Thanks for the clarification @schmidt9. – User Not Found Apr 24 '18 at 16:32
  • Thank you all for the time! – User Not Found Apr 24 '18 at 16:34

2 Answers2

1

JSONDecoder can only decode a collection type (array or dictionary) as root object.

Under the hood JSONDecoder uses JSONSerialization without any options. However to decode a String or Int you have to specify the .allowFragments option.

Use JSONSerialization with the .allowFragments option

let jsonTextContainingOneString = "1"
let data = Data(jsonTextContainingOneString.utf8)

do {
    let myString = try JSONSerialization.jsonObject(with: data, options: .allowFragments)
    print(myString)
}
catch {
    print(error)
}
vadian
  • 274,689
  • 30
  • 353
  • 361
0

Interesting... I found this:

https://github.com/apple/swift-corelibs-foundation/blob/master/Foundation/JSONSerialization.swift#L120

Specifically this guard statement:

open class JSONSerialization : NSObject {
        //...

        // top level object must be an Swift.Array or Swift.Dictionary
        guard obj is [Any?] || obj is [String: Any?] else {
            return false
        }

        //...
} 

Then I looked if a simple text-string should be considered valid JSON, and apparently it should now (it was previously not accepted as valid JSON). At least, based on this excellent answer: https://stackoverflow.com/a/7487892/8284660

This makes me wonder whether or not the behavior on my original post should be a bug or not.