I’m getting a bunch of unneeded “escaped back-slash” characters when I convert my Swift Dictionary object using JSONSerialization. It’s only happening on one of my Key-Value pairs - the one that has a URL in it:
"image" : "http:\/\/www.someWebSite.com\/images/\triangleImage.jpg”
I tried to run JSONSerialization twice thinking it might solve the problem - but it just crashes. Swift is not my main language so I’m not really sure how to fix this?
Here’s my code:
// Creating the Dictionary:
triangleDictionary["name"] = "triangle 01”
triangleDictionary["image"] = "http://www.someWebSite.com/triangleImage.jpg"
triangleDictionary["description"] = "a geometric shape"
// Serializing it to a JSON object:
do {
let triangleData = try JSONSerialization.data(withJSONObject: triangleDictionary, options: [.prettyPrinted])
let triangleDataJSONString = String(data: triangleData, encoding: .utf8)!
print("triangleDataJSONString = \(String(describing: triangleDataJSONString))")
}
catch {
print("ERROR Serializing triangleData!: \(error)")
}
The output I get is almost perfect - except for those extra back-slashes:
{
"name" : “triangle 01”,
"description" : “a geometric shape”,
"image" : "http:\/\/www.someWebSite.com\/images/\triangleImage.jpg”
}
What do I need to do to fix this?
Add this line in your code
header('Content-Type: application/json');
Related
In playgrounds, the following code produces an error:
import Foundation
struct Model: Codable {
let textBody: String
enum CodingKeys: String, CodingKey {
case textBody = "TextBody"
}
}
let json = """
{
"TextBody": "First Line\n\nLastLine"
}
""".data(using: .utf8)!
let model = try! JSONDecoder().decode(Model.self, from: json)
Fatal error: 'try!' expression unexpectedly raised an error: Swift.DecodingError.dataCorrupted(Swift.DecodingError.Context(codingPath: [], debugDescription: "The given data was not valid JSON.", underlyingError: Optional(Error Domain=NSCocoaErrorDomain Code=3840 "Unescaped control character around character 27." UserInfo={NSDebugDescription=Unescaped control character around character 27.}))): file MyPlayground.playground, line 19
The JSON above is perfectly valid according to JSONLint. So what gives?
Update:
I need a solution that will handle data returned from an API. Here is something I came up with so far, but it's gross...
if let data = data,
let dataStr = String(data: data, encoding: .utf8),
let cleanData = dataStr.replacingOccurrences(of: "\n", with: "", options: .regularExpression).data(using: .utf8)
{
do {
let response = try JSONDecoder().decode(T.Response.self, from: cleanData)
completion(.success(response))
} catch (let error) {
print(error.localizedDescription)
completion(.failure(ApiError.decoding))
}
}
Your json in your playground is represented incorrectly. It is constructed from a string literal with the \n in it. But that is being replaced newline characters in your string before it’s converted to a Data. But a newline is not permitted within JSON string. You need the two separate characters, namely \ followed by n in your string within the JSON. You can do that by escaping the \ with another \, e.g.:
let json = """
{
"TextBody": "First Line\\n\\nLastLine"
}
""".data(using: .utf8)!
Or, alternatively, in Swift 5 and later, you can use extended string delimiters, such as:
let json = #"""
{
"TextBody": "First Line\n\nLastLine"
}
"""#.data(using: .utf8)!
Or:
let json = #"{"TextBody": "First Line\n\nLastLine"}"#
.data(using: .utf8)!
I’d be very surprised if your web service was returning a JSON with newline characters (0x0a) within its string values, rather than \ character followed by n character. That would only happen if some inexperienced back-end developer was manually building JSON rather than using functions that do this properly.
You say that you’re seeing \n in Postman. That suggests that your server response is correct, that there are two characters, \ followed by n, within the string. For example, here is a web service that echoed my input back and this JSON is well formed with \ followed by n:
If your output looks like the above, then your JSON is valid and the problem in your code snippet above is merely a manifestation of how you represented this JSON in a string literal in Swift code in your playground.
You only need to worry if you see "First line on one line in this Postman “raw” view and see Lastline" on the next line (presumably with no \n).
Bottom line, we should ignore the error in your playground. Parse your actual server response (not cutting-and-copying the JSON into code, or at least not without those extended string literals). Focus on what errors, if any, you get when you parse the actual server response. I wager that if you run your parser on your actual server response, you won’t get this “Unescaped control character” error.
This question already has answers here:
Simple and clean way to convert JSON string to Object in Swift
(17 answers)
Closed 5 years ago.
I got JSON file from API but the content looks like this:
[
"https:\/\/seekingalpha.com\/article\/4125943-apple-homepod-delay-big-deal?source=feed_symbol_AAPL"
]
Suppose the JSON Object above is named as json. Then I just convert the object to string using String() method.
strings = String(json)
When I changed it to String type, it seems to get unnecessary '\n' and whitespace in it.
"[\n \"https:\\/\\/seekingalpha.com\\/article\\/4125943-apple-homepod-delay-big-deal?source=feed_symbol_AAPL\"\n]"
So it seems like the content of the JSON file is:
["\n""whitespace""whitespace""String""\n"]
When I changed it to String type, Swift just treats all the elements in it as a whole and wrapped it as a string.
My question is, how to extract the String inside so it looks like:
"https:\\/\\/seekingalpha.com\\/article\\/4125943-apple-homepod-delay-big-deal?source=feed_symbol_AAPL\"
As I am not so familiar with Swift so how to extract String or JSON Object is not easy for me. Any hints or help will be appreciated.
Swift 3,4 :
The given JSON format is Array of String.
if let json = try? JSONSerialization.jsonObject(with: data, options: .allowFragments) as? [String]{
let firstElement = json?.first ?? "Element Not Found!"
print(firstElement)
}
Swift 4:
if let json = try? JSONDecoder().decode(Array<String>.self, from: data){
let firstElement = json.first ?? "First Element Not Found!"
print(firstElement)
}
Note:
If your the Array contains more than one String. Here,urls is the class variable. i.e.,var urls = [String]()
Swift 3,4 :
if let json = try? JSONSerialization.jsonObject(with: data, options: .allowFragments) as? [String]{
if json != nil{
self.urls = json!
}
print(self.urls)
}
Swift 4:
if let json = try? JSONDecoder().decode(Array<String>.self, from: data){
self.urls = json
}
1. You will first have to convert JSON to Data
2. Convert data to string wrt to encoding
func jsonToString(jsonTOConvert: AnyObject){
do {
let data = try JSONSerialization.data(withJSONObject: jsonTOConvert, options: JSONSerialization.WritingOptions.prettyPrinted)
let convertedString = String(data: data, encoding: String.Encoding.utf8)
} catch let myJSONError {
print(myJSONError)
}
}
You are asking that a String be created with the contents:
[
"https:\/\/seekingalpha.com\/article\/4125943-apple-homepod-delay-big-deal?source=feed_symbol_AAPL"
]
The string object is doing exactly what you told it to — the thing you've asked it to represent begins with a square bracket, then there's a line break, then two spaces, etc, so the string contains a square bracket, then a line break, then two spaces, etc. There is no 'unnecessary' \n, there is only the \n you told it to put in.
If you obtained a JSON object then you need to parse it as JSON. JSONSerialization will do that job. What you've actually got is an array with a single item, which is a string. So JSONSerialization will return an array. The first item of that should be a string that is the seekingalpha.com URL.
This error has been annoying all morning, I'm trying to retrieve JSON in the format:
song = {
'artist':'Tom Waits',
'song':'New Coat Of Paint',
'lyrics':'Let\'s put a new coat of paint on this lonesome old town\nSet \'em up, we\'ll be knockin\' em [...]',
'url':'http://lyrics.wikia.com/Tom_Waits:New_Coat_Of_Paint'
}
from this URL: JSON URL
This is the function I'm using to parse the data:
func parseJSONFromData(_ jsonData: Data?) -> [String : AnyObject]?
{
if let data = jsonData {
do {
let jsonDictionary = try JSONSerialization.jsonObject(with: data, options: JSONSerialization.ReadingOptions.mutableContainers) as? [String : AnyObject]//Parses data into a dictionary
return jsonDictionary
} catch let error as NSError {
print("error processing json data: \(error.localizedDescription)")
}
}
return nil
}
}
If you go to the site http://json.parser.online.fr it will let you paste in data and check to see if it's valid JSON or not. Yours is not.
As others have said, strings need to be enclosed in double quotes, not single quotes, and the "=" is not valid.
It would be quite easy to write code that would replace all occurrences of ' with ". The song = bit is less clear. The correct format for JSON would be:
{
"song": {
"artist":"Tom Waits",
"song":"New Coat Of Paint",
"lyrics":"Let\"s put a new coat of paint on this lonesome old town\nSet \"em up, we\"ll be knockin\" em [...]",
"url":"http://lyrics.wikia.com/Tom_Waits:New_Coat_Of_Paint"
}
}
Or you could get rid of the outer dictionary entirely:
{
"artist":"Tom Waits",
"song":"New Coat Of Paint",
"lyrics":"Let\"s put a new coat of paint on this lonesome old town\nSet \"em up, we\"ll be knockin\" em [...]",
"url":"http://lyrics.wikia.com/Tom_Waits:New_Coat_Of_Paint"
}
You need to look at your data and figure out what's wrong with it in the general case that makes it not legal JSON.
EDIT:
After further digging, it seems that the URLs you're using are returning JavaScript, not JSON. Try an URL like this instead:
http://lyrics.wikia.com/wikia.php?controller=LyricsApi&method=getSong&artist=Tom%20Waits&song=new%20coat%20of%20paint
That should give you the lyrics of the Tom Waits song New Coat of Paint in well-formed JSON.
This Github page gives info on the search parameters you can use to query that site and get lyrics:
https://github.com/Wikia/app/blob/dev/extensions/wikia/LyricsApi/LyricsApiController.class.php#L10-L15
I am coding a hangman game and am loading the possible words into my app using json text files. I tried to follow the examples of others on this website but I am getting errors from Xcode.
I tried the following code based on another answer:
import Foundation
var error: NSError?
let jsonData: NSData = /* get your json data */
let jsonDict = NSJSONSerialization.JSONObjectWithData(jsonData, options: nil, error: &error) as NSDictionary
But I got an errors on line 4 with jsonDict that said "call can throw but is not marked with try, and the error is not handled" and "Type JSONReadingOptions does not conform to protocol NilLiteralConvertible".
Here is the JSON File I would like to parse:
{
“wordList” : {
“difficulty” : “Easy”
“list” : [
“fireplace”,
“apple”,
“january”,
“tooth”,
“cookies”,
“mysterious”,
“essential”,
“magenta",
“darling”,
“pterodactyl”
]}}
I would like to be able to go into my list array and get values. Thank you very much for any help!
In Swift 2 you need to use the new error handling API instead of passing a reference to an NSError:
do {
let jsonDict = try NSJSONSerialization.JSONObjectWithData(jsonData, options: NSJSONReadingOptions(rawValue: 0)) as? NSDictionary
if let jsonDict = jsonDict {
// work with dictionary here
} else {
// more error handling
}
} catch let error as NSError {
// error handling
}
You also can't pass nil as value to the options parameter, you need to pass a value of type NSJSONReadingOptions.
That said, the most common approach for parsing JSON in Swift is currently using third party libraries, such as Argo because they can save you a lot of code that is necessary to validate and safely cast the content of your JSON data to the correct Swift types.
In a JSON data file, I have a unicode character like this:
{
”myUnicodeCharacter”: ”\\u{25a1}”
}
I know how to read data from JSON files. The problem occurs when it contains characters which are represented as above.
I read it in to a String variable, myUnicodeCharacterString, which gets the value ”\u{25a1}”. I couldn't by the way use a single slash in the JSON data file because in such case it doesn't recognize the data in the file to be a proper JSON object, returning nil.
However, the value is not encoded to its graphical representation when it’s assigned to something for displaying it, for example a SKLabelNode:
mySKLabelNode.Text = myUnicodeCharacterString // displays ”\u{25a1}” and not a hollow square
The problem boils down to this:
// A: direct approach, does works
let unicodeValueByValue = UnicodeScalar("\u{25a1}") // ”9633”
let c1 = Character(unicodeValueByValue) // ”a hollow square”
// B: indirect approach, this does not work
let myUnicodeString = "\u{25a1}"
let unicodeValueByVariable = UnicodeScalar(myUnicodeString) // Error: cannot find an initialiser
let c2 = Character(unicodeValueByVariable)
So, how do I display a unicode character of the format "\u{xxxx}" if it's not directly given in code?
A better way would be to use the proper \uNNNN escape sequence
for Unicode characters in JSON (see http://json.org for details).
This is automatically handled by NSJSONSerialization, and you don't
have to convert a hex code.
In your case the JSON data should be
{
"myUnicodeCharacter" : "\u25a1"
}
Here is a full self-contained example:
let jsonString = "{ \"myUnicodeCharacter\" : \"\\u25a1\"}"
println(jsonString)
// { "myUnicodeCharacter" : "\u25a1"}
let dict = NSJSONSerialization.JSONObjectWithData(jsonString.dataUsingEncoding(NSUTF8StringEncoding)!,
options: nil, error: nil) as [String : String]
let myUnicodeCharacterString = dict["myUnicodeCharacter"]!
println(myUnicodeCharacterString)
// □
I came up with a solution, which does not answer the question, but is actually a better way of doing what I'm trying to do.
The unicode character is given instead as its hexadecimal value in the JSON data file, stripping all escape characters:
{
”myUnicodeCharacter”: ”25a1”
}
Then it's processed like this, after reading the value in to myUnicodeCharacterString:
let num = Int(strtoul(myUnicodeCharacterString, nil, 16))
mySKLabelNode.Text = String(UnicodeScalar(num))
And that worked. Now the hollow square showed up.
In Swift, we can implement the following solution
let jsonString = "{ "unicodeCharacter" : "\u00BD"}"
let data = jsonString.data(using: .utf8)
if let data = data {
let dict = try? JSONSerialization.jsonObject(with: data, options: []) as? [String: String]
print(dict?["unicodeCharacter"] ?? "")
}
Output: "½\n"