Swift String escaping when serializing to JSON using Codable
For iOS 13+ / macOS 10.15+
You can use .withoutEscapingSlashes
option to json decoder to avoid escaping slashes
let user = User(username: "John", profileURL: "http://google.com")
let jsonEncoder = JSONEncoder()
jsonEncoder.outputFormatting = .withoutEscapingSlashes
let json = try? jsonEncoder.encode(user)
if let data = json, let str = String(data: data, encoding: .utf8) {
print(str)
}
Console O/P
{"profileURL":"http://google.com","username":"John"}
NOTE: As mention by Martin R in comments \/
is a valid JSON escape sequence.
Serialize JSON string that contains escaped (backslash and double quote) Swift return Badly formed object
First of all if you wrap the JSON in the literal string syntax of Swift 4 you have to escape the backslashes.
let jsonStr = """
{
"status": "success",
"data": "{\\"name\\":\\"asd\\",\\"address\\":\\"Street 1st\\"}"
}
"""
You got nested JSON. The value for key data
is another JSON string which must be deserialized separately
let jsonData = Data(jsonStr.utf8)
do {
if let object = try JSONSerialization.jsonObject(with: jsonData) as? [String:String] {
print(object)
if let dataString = object["data"] as? String {
let dataStringData = Data(dataString.utf8)
let dataObject = try JSONSerialization.jsonObject(with: dataStringData) as? [String:String]
print(dataObject)
}
}
} catch {
print(error)
}
Or – with a bit more effort but – much more comfortable with the (De)Codable
protocol
struct Response : Decodable {
private enum CodingKeys : String, CodingKey { case status, data }
let status : String
let person : Person
init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
status = try container.decode(String.self, forKey: .status)
let dataString = try container.decode(String.self, forKey: .data)
person = try JSONDecoder().decode(Person.self, from: Data(dataString.utf8))
}
}
struct Person : Decodable {
let name, address : String
}
let jsonStr = """
{
"status": "success",
"data": "{\\"name\\":\\"asd\\",\\"address\\":\\"Street 1st\\"}"
}
"""
let jsonData = Data(jsonStr.utf8)
do {
let result = try JSONDecoder().decode(Response.self, from: jsonData)
print(result)
} catch {
print(error)
}
JSON encoding with backslashes
// This Dropbox url is a link to your JSON
// I'm using NSData because testing in Playground
if let data = NSData(contentsOfURL: NSURL(string: "https://www.dropbox.com/s/9ycsy0pq2iwgy0e/test.json?dl=1")!) {
var error: NSError?
var response: AnyObject? = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions.allZeros, error: &error)
if let dict = response as? NSDictionary {
if let key = dict["d"] as? String {
let strData = key.dataUsingEncoding(NSUTF8StringEncoding, allowLossyConversion: false)
var error: NSError?
var response: AnyObject? = NSJSONSerialization.JSONObjectWithData(strData!, options: NSJSONReadingOptions.allZeros, error: &error)
if let decoded = response as? NSDictionary {
println(decoded["IsSuccess"]!) // => 1
}
}
}
}
I guess you have to decode twice: the wrapping object, and its content.
Why does encoding a String with JSONEncoder adds a backslash?
From json.org:
A string is a sequence of zero or more Unicode characters, wrapped in
double quotes, using backslash escapes. A character is represented as
a single character string. A string is very much like a C or Java
string.
You can also red this: https://stackoverflow.com/a/27516892/614065
Decode a JSON object escaped in a String
If it's double-encoded, then you just need to double-decode. If I understand correctly, the incoming data is like this:
let str = #""{\"key\":\"value\"}""#
// "{\\"key\\":\\"value\\"}"
The first byte is "
, the second byte is {
, the third byte is \
, the fourth byte is "
.
That's a JSON-encoded string. So decode that as a string (there was a time when this didn't work because it's a "fragment," but it works fine currently, at least in all my tests):
let decoder = JSONDecoder()
let string = try! decoder.decode(String.self, from: Data(str.utf8)) // {"key":"value"}
And then decode that as your type ([String:String]
just for example):
let result = try! decoder.decode([String:String].self, from: Data(string.utf8))
// ["key": "value"]
(IMO this kind of double-encoding is fine, BTW, and I'm not sure why there are so many comments against it. Serializing an arbitrary object makes a lot more sense in many cases than forcing the schema to deal with an arbitrary structure. As long as it's cleanly encoded, I don't see any problem here.)
Serializing JSON in Swift 4 - problem figuring out data type
The issue is that multimedia
is either an array of Multimedia
objects or an empty String
. You need to write a custom initialiser for Story
to handle that.
struct Story: Decodable {
let title: String
let abstract: String
let url: String
let multimedia: [Multimedia]
private enum CodingKeys: String, CodingKey {
case title
case abstract
case url
case multimedia
}
init(from decoder:Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
title = try container.decode(String.self, forKey: .title)
abstract = try container.decode(String.self, forKey: .abstract)
url = try container.decode(String.self, forKey: .url)
multimedia = (try? container.decode([Multimedia].self, forKey: .multimedia)) ?? []
}
}
Decode html string from json
The JSON seems incorrect. There seems to be a missing "
at the end of the value of "content":
.
EDIT:
After your update, I took another look. You need to escape double quotes in a string. Weirdly enough, in this case (when the JSON is in a multi-line string), you need to escape the escaping character as well (i.e. \\
) to decode the JSON properly and get a string you can work with.
Example:
import UIKit
let json = """
{
"id": 5,
"title": "iOS and iPadOS 14: The MacStories Review",
"content": "<div id=\\"readability-page-1\\" class=\\"page\\">test<div>"
}
"""
struct ArticleModel: Codable, Identifiable {
let id: Int
let title: String
let content: String
}
let jsonData = json.data(using: .utf8)!
let article = try JSONDecoder().decode(ArticleModel.self, from: jsonData)
print(article) // ArticleModel(id: 5, title: "iOS and iPadOS 14: The MacStories Review", content: "<div id=\"readability-page-1\" class=\"page\">test<div>")
By the way, https://app.quicktype.io/ is a great tool to get decoders (and encoders) for your JSON.
Swift json decoding loses json object key order
This is not a Swift limitation per se. Both Swift and JSON Dictionaries are unordered. The JSON format does not guarantee key ordering, and as such, does not require parsers to preserve the order.
If you need an ordered collection, you'd be better off with returning an array of key-value pairs in the JSON:
{
"values": [
{"a" : ""},
{"b" : ""},
{"c" : ""},
{"d" : ""},
{"e" : ""}
]
}
And then store the keys in the right order to be able to iterate over them as you wish.
Related Topics
How to Hide the Home Indicator with Swiftui
Submitting iOS App to App Store Application Identifier Invalid
Libz.Dylib Versus Libz.1.2.3.Dylib Versus Libz.1.2.5.Dylib
How to Determine If a User Has an iOS App Installed
Drawing with Cgpath to Svg Output
How to Only Override a Method Depending on the Runtime System iOS Version
How to Dynamically Add Rows to a Specific Uitableview Section
Swift Regular Expression Format
Ms Excel Type Spreadsheet Creation Using Objective-C for iOS App
How to Prevent a Remote Notification from Being Displayed
Is Iboutletcollection Guaranteed to Be of Correct Order
Removing Wkwebview Accesory Bar in Swift
Clip Image to Square in Swiftui
How to Sort Nsmutablearray of Date Objects