# How to return native type via \`type(of:)\` from JSON string

**URL:** <https://forums.swift.org/t/how-to-return-native-type-via-type-of-from-json-string/62223>\
**Category:** Using Swift\
**Tags:** json, swift, type-casting\
**Created:** [December 26, 2022, 4:26pm UTC](https://forums.swift.org/t/how-to-return-native-type-via-type-of-from-json-string/62223 "2022-12-26T16:26:28Z")\
**Posts on this page:** 8\
**Page:** 1

<div class="post-metadata">

**Author:** ![wrmorgue](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/wrmorgue/32/23966_2.png) [@wrmorgue](https://forums.swift.org/u/wrmorgue)\
**Post date:** [December 26, 2022, 4:26pm UTC](https://forums.swift.org/t/how-to-return-native-type-via-type-of-from-json-string/62223/1 "2022-12-26T16:26:28Z")

</div>

I'm trying to print a data type for a JSON string, but I get non-native types:

```swift

func printJSONTypes(jsonString: String) {
  // First, try to parse the JSON string into an object
  guard let jsonObject = try? JSONSerialization.jsonObject(with: jsonString.data(using: .utf8)!) else {
    print("Error: Unable to parse JSON string")
    return
  }

  // If the JSON object is a dictionary, iterate over its keys and values
  if let jsonDictionary = jsonObject as? [String: Any] {
    for (key, value) in jsonDictionary {
      print("\(key): \(type(of: value))")
    }
  }
}

let jsonString = """
{
  "name": "John",
  "age": 30,
  "isEmployed": true
}
"""

printJSONTypes(jsonString: jsonString)

// Output:
// name: NSTaggedPointerString
// age: __NSCFNumber
// isEmployed: __NSCFBoolean

```

Here I expect to get a native data type, but the `type(of:)` function returns runtime type.

I tried just comparing each data type `switch case is String`, but this way seems a bit dumb to me, because I would have to go through all the possible cases.  
`Mirror` also outputs non-native types.

Any ideas?

---

<div class="post-metadata">

**Author:** ![QuinceyMorris](https://avatars.discourse-cdn.com/v4/letter/q/d26b3c/32.png) [@QuinceyMorris](https://forums.swift.org/u/QuinceyMorris)\
**Post date:** [December 26, 2022, 6:53pm UTC](https://forums.swift.org/t/how-to-return-native-type-via-type-of-from-json-string/62223/2 "2022-12-26T18:53:29Z")

</div>

`JSONSerialization` (see [Apple Developer Documentation](https://developer.apple.com/documentation/foundation/jsonserialization)) really is an Obj-C object (`NSJSONSerialization`) and so are all of its JSON object types:

- All objects are instances of [`NSString`](https://developer.apple.com/documentation/foundation/nsstring), [`NSNumber`](https://developer.apple.com/documentation/foundation/nsnumber), [`NSArray`](https://developer.apple.com/documentation/foundation/nsarray), [`NSDictionary`](https://developer.apple.com/documentation/foundation/nsdictionary), or [`NSNull`](https://developer.apple.com/documentation/foundation/nsnull).

The class names are various implementation-level details of those Obj-C classes, which don't translate "prettily" into Swift types.

My question here is what are you trying to do with the types? Yes, you might want to display them, where "prettiness" is significant, but just about anything else you want to do will involve testing the type against one or more expected Swift types, for which you're going to extract a value that you can test with `is` or `as?`.

---

<div class="post-metadata">

**Author:** ![wrmorgue](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/wrmorgue/32/23966_2.png) [@wrmorgue](https://forums.swift.org/u/wrmorgue)\
**Post date:** [December 26, 2022, 7:56pm UTC](https://forums.swift.org/t/how-to-return-native-type-via-type-of-from-json-string/62223/3 "2022-12-26T19:56:23Z")

</div>

> [@QuinceyMorris](#):
>
> My question here is what are you trying to do with the types?

Just trying to create a utility that generates a data model from a JSON response, so I need native data types.

---

<div class="post-metadata">

**Author:** ![tera](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/tera/32/38201_2.png) [@tera](https://forums.swift.org/u/tera)\
**Post date:** [December 26, 2022, 11:21pm UTC](https://forums.swift.org/t/how-to-return-native-type-via-type-of-from-json-string/62223/4 "2022-12-26T23:21:24Z")

</div>

> [@wrmorgue](#):
>
> I tried just comparing each data type `switch case is String`, but this way seems a bit dumb to me, because I would have to go through all the possible cases.

Of which there is only a handful: Bool, Double, String, Array and Dictionary. Also Int should you wish to distinguish json ints from doubles. You'll need to decide what type to assign to `nil` if that can be encountered in your json files.

---

<div class="post-metadata">

**Author:** ![wrmorgue](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/wrmorgue/32/23966_2.png) [@wrmorgue](https://forums.swift.org/u/wrmorgue)\
**Post date:** [December 27, 2022, 6:49am UTC](https://forums.swift.org/t/how-to-return-native-type-via-type-of-from-json-string/62223/5 "2022-12-27T06:49:41Z")

</div>

> [@tera](#):
>
> Bool, Double, String, Array and Dictionary

If I rewrite the function like this:

```swift

func matchTypes(_ jsonString: String) -> String {
    guard let jsonObject = try? JSONSerialization.jsonObject(with: jsonString.data(using: .utf8)!) else { return String() }
    guard let jsonDictionary = jsonObject as? [String: Any] else { return String() }
    
    var result = ""
    
    for (key, value) in jsonDictionary {
        result += "\tlet \(key): "
        switch value {
        case is Int: result += "Int\n"
        case is String: result += "String\n"
        case is Double: result += "Double\n"
        case is Bool: result += "Bool\n"
        // 'Array' requires arguments in <...>
        // case is Array: result += "Array\n"
        // 'Dictionary' requires arguments in <...> | <Key: Hashable, Any>
        // case is Dictionary: result += "Dictionary\n"
        default: result += "\(type(of: value))\n"
        }
    }
    return result
}

let jsonString = """
{
  "name": "John",
  "age": 30,
  "isEmployed": true
}
"""

print(matchTypes(jsonString))

// let age: Int
// let name: String
// let isEmployed: Int

// isEmployed is Bool, not Int 🥲

```

then it turns out that I need to check every case

```swift
case is Array<Int>
case is Array<String>
case is Array<Float>
// etc

case is Dictionary<String, String>
case is Dictionary<String, Int>
// etc

```

You can of course just specify `Array<Any>`, but that's not quite right.

---

<div class="post-metadata">

**Author:** ![pyrtsa](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/pyrtsa/32/740_2.png) [@pyrtsa](https://forums.swift.org/u/pyrtsa)\
**Post date:** [December 27, 2022, 7:10am UTC](https://forums.swift.org/t/how-to-return-native-type-via-type-of-from-json-string/62223/6 "2022-12-27T07:10:30Z")

</div>

The result of `JSONSerialization.jsonObject` represents booleans and numbers all as the class cluster type `NSNumber`.

As you see, `NSNumber` quickly loses the underlying type information when used in further arithmetic or casted to more native Swift types, and happily treats its encoded `true` as the number one if asked for an integer.

But you can check the boolean values first in the way explained here: [JSONSerialization turns Bool value to NSNumber - #3 by eskimo](https://forums.swift.org/t/jsonserialization-turns-bool-value-to-nsnumber/31909/3)

---

<div class="post-metadata">

**Author:** ![tera](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/tera/32/38201_2.png) [@tera](https://forums.swift.org/u/tera)\
**Post date:** [December 27, 2022, 7:59am UTC](https://forums.swift.org/t/how-to-return-native-type-via-type-of-from-json-string/62223/7 "2022-12-27T07:59:23Z")

</div>

> [@wrmorgue](#):
>
> ```swift
> case is Array<Int>
> case is Array<String>
> case is Array<Float>
> // etc
> 
> case is Dictionary<String, String>
> case is Dictionary<String, Int>
> // etc
> 
> ```

Ah, no, don't do that, as you'd also need to match [[String]], [[[String]]], [[[[String]]]] ad infinitum.

> [@](#):
>
> You can of course just specify `Array<Any>`, but that's not quite right.

Yes, this is correct approach: [Any] or [Any?]. Once you have the array or dictionary match - recurse:

```swift
func match(_ value: Any?) {
    guard let value = value else {
        print("nil")
        return
    }
    switch value {
    case let v as Bool:
        print("Bool \(v)")
    case let v as Int:
        print("Int \(v)")
    case let v as Double:
        print("Double \(v)")
    case let v as String:
        print("String \(v)")
    case let elements as [Any?]:
        print("Array with \(elements.count) values")
        for (index, element) in elements.enumerated() {
            print(" element[\(index): ", terminator: "")
            match(element)
        }
    case let elements as [String: Any?]:
        print("Dictionary with \(elements.count) values")
        for (key, value) in elements {
            print(" key[\(key)]: ", terminator: "")
            match(value)
        }
    default:
        fatalError("TODO")
    }
}

```

---

<div class="post-metadata">

**Author:** ![taylorswift](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/taylorswift/32/43413_2.png) [@taylorswift](https://forums.swift.org/u/taylorswift)\
**Post date:** [December 27, 2022, 9:04pm UTC](https://forums.swift.org/t/how-to-return-native-type-via-type-of-from-json-string/62223/8 "2022-12-27T21:04:21Z")

</div>

> [@tera](#):
>
> Also Int should you wish to distinguish json ints from doubles.

JSON is untyped, so eagarly narrowing JSON numerics to integers is usually a bad idea, because an array of doubles like `[1, 1.5, 2]` could become heterogenous when imported into swift.
