I'm using Swift to parse JSON strings. This is the code:
var jsonStr = "..." // JSON string
var data = jsonStr.dataUsingEncoding(NSUTF8StringEncoding)
var error: NSError?
var array = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions.MutableContainers, error: &error) as [AnyObject]
And this code works almost all the time. But today I tried to parse a new string input but it crashed my app in the last line. I couldn't even see the error message in the "error" variable... It crashed before that variable as updated with the error info.
The JSON string that I'm trying to parse is here: http://pastebin.com/wf6jtNhf
I'm confident that my JSON string is valid for two reasons:
I validated the input using jsonlint.com
I created a Objective-C version of the code above and it successfully parsed the same input
Can anybody see a reason why I can't parse this string or should I assume that the NSJSONSerialization class is bugged in Swift? I'm using Xcode Beta 3.
Edit 1:
Apparently there is a lot of UTF-16 characters in my string (emoji characters). Is there a key to parse the string keeping those characters? I tried NSUTF16StringEncoding the code below, but it didn't work:
var data = jsonStr.dataUsingEncoding(NSUTF16StringEncoding)
Edit 2:
I posted this same question in the Apple Developer forum and apparently there is indeed a bug in the Swift version of NSJSONSerialization.JSONObjectWithData() when there are emoji characters in the data. I hope this gets fixed in the final version.
Also, changing my variable from [AnyObject] to as? Dictionary, as some suggested below, didn't crash my app anymore.
Testing this on my computer, you're crashing because you're casting as [AnyObject], and you're getting a nil result back out. If you change that to as? [AnyObject] then it will allow the return value to be nullable and you'll then be able to print the error.
As for the reason it's failing to parse, I'm not quite sure yet. The culprit seems to be the following chunk, not sure why it's invalid, perhaps some of the UTF is giving the parsing code problems.
"id": "#babiminkah",
"nome": "B\u00E1rbara Santos",
"imagem": "http://pbs.twimg.com/profile_images/490247572175327233/w4dXqfPm_bigger.jpeg",
"texto": "amanh\u00E3 tem b\u00F3 do catarina..... na friends \uD83D\uDE02\uDE02\uD83D\uDE02\uDE02\uD83D\uDE02\uDE02\uD83D\uDE02\uDE02\uD83D\uDE02\uDE02\uD83D\uDE02\uDE02\uD83D\uDE02\uDE02\uD83D\uDE02\uDE02\uD83D\uDE02\uDE02\uD83D\uDE02\uDE02\uD83D\uDE02\uDE02\uD83D\uDE02\uDE02",
"horario": "2014-07-18 17:43:04"
}
Edit1: Ripping out more pieces of that chunk, it is the texto field's value that's causing the problem. If you remove all of the value after and including \uD83D it will work. These unicode values seem to be the problem.
Edit2: According to this, http://www.fileformat.info/info/unicode/char/d83d/index.htm , D83D is not a valid unicode character. It seems likely some of the subsequent ones are not either.
Related
I have a server written in Rust, this server gets a request in JSON, the JSON the server is getting is a string and sometimes users write quotes inside the value. For example when making a new forum thread.
The only thing I really need to do is to escape the quotes inside the value.
So this:
"{"name":""test"", "username":"tomdrc1", "date_created":"07/12/2019", "category":"Developer", "content":"awdawdasdwd"}"
Needs to be turned into this:
"{"name":"\"test\"", "username":"tomdrc1", "date_created":"07/12/2019", "category":"Developer", "content":"awdawdasdwd"}"
I tried to replace:
let data = let data = "{"name":""test"", "username":"tomdrc1", "date_created":"07/12/2019", "category":"Developer", "content":"awdawdasdwd"}".to_string().replace("\"", "\\\"");
let res: serde_json::Value = serde_json::from_str(&data).unwrap();
But it results in the following error:
thread '' panicked at 'called Result::unwrap() on an Err value: Error("key must be a string", line: 1, column: 2)
I suspect because it transforms the string to the following:
let data = "{\"name\":\"\"test\"\", \"username\":\"tomdrc1\", \"date_created\":\"07/12/2019\", \"category\":\"Developer\", \"content\":\"awdawdasdwd\"}"
If I understand your question right, the issue is that you are receiving strings which should be JSON but are in fact malformed (perhaps generated by concatenating strings).
If you are unable to fix the source of those non-JSON strings the only solution I can think of involves a lot of heavy lifting with caveat:
Writing a custom "malformed-JSON" parser
Careful inspection/testing/analysis of how the broken client is broken
Using the brokenness information to fix the "malformed-JSON"
Using the fixed JSON to do normal request processing
I would recommend not to do that except for maybe a training excercise. Fixing the client will be done in minutes but implementing this perfectly on the server will take days or weeks. The next time this one problematic client has been changed you'll have to redo all the hard work.
The real answer:
Return "400 Bad Request" with some additional "malformed json" hint
Fix the client if you have access to it
Additional notes:
Avoid unwrapping in a server
Look for ways to propagate the Result::Err to caller and use it to trigger a "400 Bad Request" response
Check out error handling chapter in the Rust book for more
After an Xcode update I encountered a weird problem. In my application, when I get a response from an API, I parse it manually and map it to my model. There are a lot of places in my code where I cast a JSON value to Float with null coalescing like below:
randomVariable = jsonDict["jsonKey"] as? Float ?? 0
It used to work fine but after the update, it would always default to 0. In order to determine the cause, I tried force casting it to Float like below
randomVariable = jsonDict["jsonKey"] as! Float
This resulted in the following error
Unable to bridge NSNumber to Float
Upon further investigation, I found out that Xcode is using Swift 3.3 (previously, it was using Swift 3.2). The solutions I found on StackOverflow revolve around casting it to NSNumber instead of Float. As I mentioned above, I have similar lines of code spread across my app. I was wondering if there is a way to fix this issue. Maybe using an extension of some sort?
As you have found, you may need to cast it first to NSNumber.
Something like this:
randomVariable = (jsonDict["jsonKey"] as? NSNumber)?.floatValue ?? 0
Maybe regex replacement would help you update your code.
Pattern: jsonDict\[([^\]]*)\] as\? Float
Replace with: (jsonDict[$1] as? NSNumber)?.floatValue
The problem is in 32 bit architecture and updated NSNumber class in swift 4.1.
For example when I write number 1.1 into dictionary it will be stored as 64bit NSNumber with most accurate representation = 1.10000000000000008881784197001...
So, when you read this number as! Float it will fail because 32 bit precision is not good enough to get this most accurate representation.
The most accurate representation in 32 bit = 1.10000002384185791015625
64 bit presentation of float number
So, you need to get truncated result that is implemented inside NSNumber.floatValue() function.
randomVariable = (jsonDict["jsonKey"] as! NSNumber).floatValue
Before I discovered SwiftyJSON, I used to do this, parsing the data manually. You may want to check their source code under the hood to see how it parses your data.
// Sample: rate is a Float
// Number
if let number = dictionary[SerializationKeys.rate] as? NSNumber {
print("IS A NUMBER")
rate = number.floatValue
}
// String
if let string = dictionary[SerializationKeys.rate] as? String {
print("IS A STRING")
let decimalNumber = NSDecimalNumber(string: string)
rate = decimalNumber.floatValue
}
And since I've mentioned the SwiftyJSON, I'd like to recommend it to avoid such headache. Leave the parsing of json to that guy. https://github.com/SwiftyJSON/SwiftyJSON
I hope it helps.
EDITED:
I ended up asking the vendor to change the implementation of sending the JSON
I want to parse a JSON string into a Dictionary using swift.
The plist key has the following value:
"{runid:\"8090\",status_id:\"5\"}"
and when I convert this into a String object, it looks like this "\"{runid:\\\"8488\\\",testids:[\"7480769\"]}\""
Code
let data = theString.data(using: .utf8)
let jsonObject = try! JSONSerialization.jsonObject(with: data!, options: .allowFragments)
I have already gone through various posts and am not able to use the conventional solution we use in our daily lives.
Following things I already know:
The keys are not properly formatted in quotes
This is ideally not structured in the conventional way for parsing.
NOTE
Important thing to note is that I will not be able to change the format, because this value is coming from a third party vendor.
Another thing to note is that this string is being successfully parsed by the JAVA team in the company
I don't know if it covers all cases, but this is a solution using Regular Expression.
It searches for a pattern
{ or ,
one or more alphanumeric characters
:
and captures the first and second condition. Then it replaces the found match by adding the quotes:
let theString = "{runid:\"8090\",status_id:\"5\"}"
let validJSONString = theString.replacingOccurrences(of: "([{,])(\\w+):", with: "$1\"$2\":", options: .regularExpression)
print(validJSONString)
Blame the third party vendor for that mess. Things won't change if nobody complains.
I am facing one issue but it occurs randomly any time not always. I am not sure what is the issue.
What I am trying is I have an JSON string and I am converting it to DBObject and then push/insert it to mongo DB. below is the code for same.
Here final_json_str is JSON string which I am reading it from input file and then converting it to DBOject and then inserting it to Mongo DB.
val dbObject: DBObject = JSON.parse(final_json_str).asInstanceOf[DBObject]
But some time I am getting error as CastClassException cannot convert java.lang.String to com.mongodb.DBObject.
Can anyone one help me, Am i doing some thing wrong in this. Please let me know if more details are required.
Thanks a lot in advance
from the signature of JSON.parse
public static Object parse(final String jsonString)
One can see it can actually return any Object. Looking a bit further in the actual code, the parse will choose what method to return with a big switch on the first character of your String.
In the case it matches either ' or " the returned Object will be a String which you try to cast into a DBObject.
I found a tutorial for server implementation in a game on this link:
http://unity-tutorials.blogspot.in/
I implemented the code for my server sending in the following data on the Login Section to my Server:
{"email":"rudi#mrpatch.co", "pass": "mrpatch"}
This server is implementing JSON and is giving the below response :
Receive response: "{\"status\":\"success\",\"data\":[{\"id\":\"1\",\"email\":\"rudi#mrpatch.co\",\"password\":\"mrpatch\",\"first_name\":\"Rudi\",\"last_name\":\"Ullon\",\"birth_date\":\"1981-03-20\",\"status\":\"1\"}]}"
There is a JSON parser script in this project, which returns System.string , this is being used by a parser in my other script LoginService.js
But while I am trying to store this in a Boo.Lang.Hash (hashtable) it gives me error in following code:
var parsed : Boo.Lang.Hash = JSONParse.JSONParse(httpResponse.text);
This is the Error message I am getting:
InvalidCastException: Cannot cast from source type to destination type.
LoginService+$sendLoginData$6+$.MoveNext () (at Assets/Scripts/StartMenu/LoginService.js:61)
I have tried saving it a Boo.Lang.Hash, as String etc. but nothing seems to be working.
if you have json you can do in this way.
var parsed : Boo.Lang.Hash = JSONParse.JSONParse(your json );
Oh boy...
You seem to have quite some conceptual issues with type casting and that's a far too broad topic (joke link). :P
The error message means you can't convert from String to that Hashtable through casting. You need a function to convert it for you or do it yourself.
The JSONParse you mentioned can actually do it:
var jsonData : json = json.fromString(httpResponse.text);
At least now you have jsonData.values which can be easily converted to a Hashtable...
But keep in mind "Hashtables" are obsolete and even the Unity Player will warn you about it. Read more.
you must use the JsonPare.js from source code on the demo.https://docs.google.com/file/d/0B0HipNssJJD-bEh0Wi1XeV9PSlE/edit