简体   繁体   中英

Swift JSON Decodable \u0000

Problem

I'm currently getting JSON from a server that I don't have access to. The JSON I sometimes get will put this character \ at the end of a String. As a result my decoding fails because this character just fails it.

I'm trying to debug this in Playground but I keep getting this error.

Expected hexadecimal code in braces after unicode escape

Here is some sample code to try out.

import UIKit
import Foundation

struct GroceryProduct: Codable {
    var name: String
}

let json = """
{
    "name": "Durian \u0000"
}
""".data(using: .utf8)!

let decoder = JSONDecoder()
let product = try decoder.decode(GroceryProduct.self, from: json)

print(product.name)

Question

How do you deal with \ from JSON? I have been looking at DataDecodingStrategy from the Apple documentation but I can't even test anything out because the Playground fails to even run.

Any direction or advice would be appreciated.


Update

Here is some more setup code to tryout in your Playground or a real app.

JSON test.json

{
    "name": "Durian \u0000"
}

Code

extension Bundle {
    func decode<T: Decodable>(_ type: T.Type, from file: String, dateDecodingStrategy: JSONDecoder.DateDecodingStrategy = .deferredToDate, keyDecodingStrategy: JSONDecoder.KeyDecodingStrategy = .useDefaultKeys) -> T {
        
        guard let url = self.url(forResource: file, withExtension: nil) else {
            fatalError("Failed to locate \(file) in bundle.")
        }

        guard let data = try? Data(contentsOf: url) else {
            fatalError("Failed to load \(file) from bundle.")
        }
                
        let decoder = JSONDecoder()
        decoder.dateDecodingStrategy    = .deferredToDate
        decoder.keyDecodingStrategy     = .useDefaultKeys

        do {
            return try decoder.decode(T.self, from: data)
        } catch DecodingError.keyNotFound(let key, let context) {
            fatalError("Failed to decode \(file) from bundle due to missing key '\(key.stringValue)' not found – \(context.debugDescription)")
        } catch DecodingError.typeMismatch(_, let context) {
            fatalError("Failed to decode \(file) from bundle due to type mismatch – \(context.debugDescription)")
        } catch DecodingError.valueNotFound(let type, let context) {
            fatalError("Failed to decode \(file) from bundle due to missing \(type) value – \(context.debugDescription)")
        } catch DecodingError.dataCorrupted(_) {
            fatalError("Failed to decode \(file) from bundle because it appears to be invalid JSON")
        } catch {
            fatalError("Failed to decode \(file) from bundle: \(error.localizedDescription)")
        }
    }
}


struct GroceryProduct: Codable {
    var name: String
}

// Try running this and it won't work
let results = Bundle.main.decode(GroceryProduct.self, from: "test.json")


print(results.name)

You need to escape \ characters first - this can be done before decoding:

guard let data = try? Data(contentsOf: url) else {
    fatalError("Failed to load \(file) from bundle.")
}

let escaped = Data(String(data: data, encoding: .utf8)!.replacingOccurrences(of: "\0", with: "").utf8)
...
return try decoder.decode(T.self, from: escaped)

Note: force-unwrapping for simplicity only.


In Playground you can escape it with an additional \\ (to make it work):

let json = """
{
    "name": "Durian \\u0000"
}
""".data(using: .utf8)!

or replace it with \\0 (to make it fail - behave like during the decoding):

let json = """
{
    "name": "Durian \0"
}
""".data(using: .utf8)!

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM