swift json serialization error : Unable to convert data to string - json

I'm a Korean developer, and I'm not familiar with swift.
Unfortunately the server I need to connect with is encoded in euc-kr format.
If the JSON contains Korean, it outputs the following error: Unable to convert data to string
I have verified through Json Parser that the shape of my JSON is not strange.
I am using Alamofire library for server connection.
This is my Json structure.
{"result":[{"id":"2659","member_id":"sexyback","nickname":"BJ%ED%8F%AC%EB%A1%9C%EB%A6%AC","live_YN":"N","19":"all","intro":"엄청나게 재미있는 포로리","fan":"open","acc_good":"","acc_fav":"","img_url":"%2Fpx%2Ffiles%2Fdotv_2659_f1_657.jpg"}],"total":[{"total":"6"}],"status":[{"status":"success"}]}
My logic is:
static func requestHotBJList() async -> Any {
let url = WebConstant.getHotBJList()
DLogUtil.debug(tag: #file, content: url)
do {
let data = try await HttpWrapper.requestGetTest(withUrl: url)
return JSONUtil.createJSONObject(data: data)
} catch {
return error
}
}
Get data from server via Http Wrapper.request Get. Try JSON Parsing through JSONUtil.createJSONObject(data: data) with the data of the Data type imported from here.
I get data from the server in the following way:
public static func requestGet(
withUrl url: String,
withHeader header: HTTPHeaders? = nil
) async throws -> Data {
try await withUnsafeThrowingContinuation { continuation in
AF.request(url, method: .get, headers: header).validate().responseData { response in
if let data = response.data {
continuation.resume(returning: data)
return
}
if let error = response.error {
continuation.resume(throwing: error)
return
}
fatalError("fatal error")
}
}
}
The data parsing logic is as follows :
public static func createJSONObject(data: Data) -> [String : Any] {
do {
DLogUtil.debug(tag: #file, content: "data ? \(data)")
let json = try JSONSerialization.jsonObject(with: data, options: []) as! [String : Any]
return json
}
catch let error as NSError {
DLogUtil.debug(tag: #file, content: error)
return [String : Any]()
}
}
Error is output during the parsing process. I've searched a lot, but I can't find it.

With help from #workingdogsupportUkraine, I found a way. After converting data type to NSString, NSString is encoded in utf-8 format.
var dataString = NSString(data: data, encoding: CFStringConvertEncodingToNSStringEncoding(0x0422))
var datadata = dataString!.data(using: String.Encoding.utf8.rawValue)
let json = try JSONSerialization.jsonObject(with: datadata!, options: []) as! [String : Any]
return json

Related

I need help parsing some JSON data

I am fairly new to parsing json data and I am attempting to parse some json data from an rss feed generator and I am running into a problem where I can successfully print the data I am getting but I can't save the data to an object.
I have looked through tutorials that used decodables/codables mostly but I was able to use the urlSession and jsonSerialization objects for what I needed just fine.
class JSONSongs {
// initialize song array...
var songArray: [Song] = []
func getSongs() {
let jsonSongUrl = "https://rss.itunes.apple.com/api/v1/us/apple-music/top-songs/all/50/explicit.json"
let songUrl = URL(string: jsonSongUrl) // convert string to usable url
// start url session task with apple music api url...
// we get some data(hopefully), a response code and an error(hoepfully not)
let songTask = URLSession.shared.dataTask(with: songUrl!) { (data, response, error) in
// checking for an error
if error != nil {
print(Error.self)
print(error?.localizedDescription)
return
} else {
// lets store our data in a variable
if let content = data {
do {
// taking the json data and converting it so we can make objects
let json = try JSONSerialization.jsonObject(with: content, options: JSONSerialization.ReadingOptions.mutableContainers)
//print(json) // making sure data is present
// checking to see if our json data is there
guard let jsonOne = json as? [String: Any] else {
print("invalid operation!")
return
}
// accessing top root of the json file
if let feed = jsonOne["feed"] as? [String: Any] {
//print("it worked") // testing
// accessing the results array where the albums are stored
// there are arrays in the nested json data so we need the double brackets to access them
if let result = feed["results"] as? [[String: Any]]{
for item in result {
// attempting to store data in Song object, this is where problems appear
if let songName = (item["name"] as AnyObject? as? String),
let artistName = (item["artistName"] as AnyObject? as? String),
let coverArt = (item["artworkUrl100"] as AnyObject? as? String),
let artistPage = (item["artistUrl"] as AnyObject? as? String) {
self.songArray.append(Song(songName: songName, artistName: artistName, coverArt: coverArt, artistPage: artistPage))
// printing the data to the console works here but I can't save the data to an object
}
}
}
}
} catch {
print(error.localizedDescription)
print(Error.self)
return
}
}
}
}
songTask.resume()
}
}
All I get is either nil when I try and print a string value or 0 when I try and count the number of objects that are present in the songArray array
Basically your code is correct and should work, however this is a version using Decodable.
The songs property will contain the song data
struct Root : Decodable {
let feed : Feed
}
struct Feed : Decodable {
let results : [Song]
}
struct Song : Decodable {
let name, artistName : String
let artworkUrl100, artistUrl : URL
}
class JSONSongs {
var songs = [Song]()
func getSongs() {
let jsonSongUrl = "https://rss.itunes.apple.com/api/v1/us/apple-music/top-songs/all/50/explicit.json"
let songUrl = URL(string: jsonSongUrl) // convert string to usable url
// start url session task with apple music api url...
// we get some data(hopefully), a response code and an error(hoepfully not)
let songTask = URLSession.shared.dataTask(with: songUrl!) { [weak self] data, _, error in
// checking for an error
if let error = error { print(error); return }
do {
// taking the json data and converting it so we can make objects
let result = try JSONDecoder().decode(Root.self, from: data!)
self?.songs = result.feed.results
print(self?.songs)
} catch {
print(error)
}
}
songTask.resume()
}
}

Importing Firebase causes JSON fetching ambiguous

I have been fetching JSON in my View Controller, and I needed a function to add data in that same VC to firebase, so imported Firebase ( Pods firebase core, auth and firestore ) and now it gives me an error on JSON fetching that it's Ambiguous use of 'subscript'
func getDetails(link: URL!) {
var plot : String = " "
let task = URLSession.shared.dataTask(with: link!) { (data, response, error) in
if error != nil
{
print("error")
}
else
{
if let content = data
{
do
{
//JSON results
let myJson = try JSONSerialization.jsonObject(with: content, options: JSONSerialization.ReadingOptions.mutableLeaves) as AnyObject
//myJson ~~~ ["Plot"] Ambiguous use of 'subscript'
plot = myJson["Plot"] as! String
}
catch
{
print("error in JSONSerialization")
}
}
}
}
task.resume()
DispatchQueue.main.asyncAfter(deadline: .now() + 0.5, execute: {
self.plot.text = plot
})
}
I would love to maintain ability to pick the "Plot" value of JSON and have the firebase running
Your problem is casting result from JSONSerialization to AnyObject. If you want to be able to use subscript, you should, in your case, downcast result to dictionary type, for example [String:Any]
if let myJson = try JSONSerialization.jsonObject(with: content, options: .mutableLeaves) as? [String:Any] {
// plot = myJson["Plot"] as? String ?? "Default value"
}
Anyway, rather learn something about Codable and use it instead of JSONSerialization. Just create class/struct conforming to Decodable protocol and then use JSONDecoder to decode Data object.
Here's how I would rewrite this method, given a ModelObject struct or class that represents your server response.
func getDetails(link: URL!) {
var plot = " "
let group = DispatchGroup()
group.enter()
let task = URLSession.shared.dataTask(with: link!) { (data, response, error) in
defer { group.leave() }
guard error == nil else {
print(error)
return
}
if let content = data {
do {
let modelObject = try JSONDecoder().decode(ModelObject.self, from: data)
plot = modelObject.plotString
}
catch {
print(error)
}
}
}
task.resume()
group.notify(queue: DispatchQueue.main) {
self.plot.text = plot
}
}

Stringified JSON to Swift Object or JSON dictionary

In my Swift code I make a URLRequest to my node.js server:
URLSession.shared.dataTask(with: checkoutRequest, completionHandler: {
[weak self] (data: Data?, response: URLResponse?, error: Error?) in
guard let data = data,
let dataString = String(data: data, encoding: String.Encoding.utf8) else {
return
}
// Help me!!!
}).resume()
The node.js handles this request by processing a transaction through the Braintree Payments checkout API.
checkoutProcessor.processCheckout(amount, nonce, (error, result) => {
// Checkout callback
if (error) {
res.write(error.message)
res.end()
} else if (result) {
console.log(result)
res.write(JSON.stringify(result))
res.end()
}
})
As usual, if the API request fails (e.g., no signal) it returns an error but if the transaction goes through, it returns a result.
The type of the result, however, depends on whether the financial transaction fails or succeeds:
For example, the result for a successful transaction:
Object {transaction: Transaction, success: true}
result for failed transaction:
ErrorResponse {errors: ValidationErrorsCollection, params: Object, message: "Insufficient Funds", transaction: Transaction, success: false}
The dataString winds up looking like this:
{\"transaction\":{\"id\":\"m7mj3qd7\",\"status\":\"submitted_for_settlement\",\"type\":\"sale\",\"currencyIsoCode\":\"USD\",\"amount\":\"12.34\",\"merchantAccountId\":\"yourpianobar\",\"subMerchantAccountId\":null,\"masterMerchantAccountId\":null,\"orderId\":null,\"createdAt\":\"2018-09-19T03:30:27Z\",\"updatedAt\":\"2018-09-19T03:30:27Z\",\"customer\":{\"id\":\"622865439\",\"firstName\":\"Test\",\"lastName\":\"FromSwiftTest\"
which certainly resembles a JSON object but I can't seem to decode it with JSONDecoder, doing so fails. (JSONEncoder also fails)
Most solutions I see for Objectifying stringified JSON data into Swift involves writing a swift struct into which to plop all the JSON object's properties, but since this the data structure of the result is unknown on the swift end, I don't know what to do.
How do I get these objects into my swift code?
Note: I've also tried just sending res.send(result) in the node.js code but that doesn't really change anything.
This would do the trick for Swift 5:
if let data = dataString.data(using: String.Encoding.utf8) {
do {
if let dictionary = try JSONSerialization.jsonObject(with: data, options: []) as? [String:Any] {
// Use this dictionary
print(dictionary)
}
} catch _ {
// Do nothing
}
}
You can use JSONSerialization class on your data to convert it from data to Dictionary/Array based on your json response. The code can look something like below (based on my understanding) in swift 4
URLSession.shared.dataTask(with: checkoutRequest) { (data, response, error) in
guard let requestData = data, error == nil, let httpResponse = response as? HTTPURLResponse else {
// handle error
return
}
do {
if httpResponse.statusCode == 200 {
// success json
let jsonObject = try JSONSerialization.jsonObject(with: requestData, options: .allowFragments)
print(jsonObject) // jsonObject can be dictionary or Array. Typecast it based on your response
} else {
//error json
let jsonObject = try JSONSerialization.jsonObject(with: requestData, options: .allowFragments)
print(jsonObject) // jsonObject can be dictionary or Array. Typecast it based on your response
}
}
catch {
print(error.localizedDescription)
}
}.resume()

How do I access a nested JSON value using Alamofire and SwiftyJSON?

I am trying to access nested JSON results using swiftyJSON and Alamofire. My print value is nill and I believe I am not doing this correctly. What should my parameters be? I am trying to get the quote value located at http://quotes.rest/qod.json
func getAPI() {
Alamofire.request(.GET, "http://quotes.rest/qod.json", parameters: ["contents": "quotes"])
.responseJSON { response in
if let JSON = response.result.value {
print(JSON["quote"])
}
}
}
In your JSON quotes is an array so if you want to access quote of the first object you should do it by accessing first object:
func getAPI() {
Alamofire.request(.GET, "http://quotes.rest/qod.json", parameters: ["contents": "quotes"])
.responseJSON { response in
if let jsonValue = response.result.value {
let json = JSON(jsonValue)
if let quote = json["contents"]["quotes"][0]["quote"].string{
print(quote)
}
}
}
}
If the syntax of the json isn't correct, since it is fully printed anyway you should notice what's wrong.
func getAPI() {
Alamofire.request(.GET, "http://quotes.rest/qod.json", parameters: ["contents": "quotes"])
// JSON response
.responseJSON { response in switch response.result {
case .Failure(let error):
// got an error in getting the data, need to handle it
print("error calling GET, json response type :")
// print alamofire error code
let statusCode = error.code
print("error code json : \(statusCode)")
// print json response from server
if let data = response.data {
print("Response data: \(NSString(data: data, encoding: NSUTF8StringEncoding)!)")
}
// print http status code plus error string
print(NSHTTPURLResponse.localizedStringForStatusCode(statusCode))
if let httpResponse : NSHTTPURLResponse = response.response {
print("HTTP Response statusCode: \(httpResponse.statusCode)")
}
case .Success( _):
let statusCode = (response.response?.statusCode)!
print("status code json : \(statusCode)")
print("there is a response json")
//print(value)
// parse the result as JSON, since that's what the API provides and save datas as new user in coreData
guard let data = response.data else {
print("Error parsing response data")
return
}
let json = JSON(data: data)
// access first element of the array
if let postContent = json["contents"]["quotes"][0]["quote"].string{
// deal with json
}
}
}

Issue converting NSData Object into NSDictionary

I'm trying to extract information from a JSON file. Is it possible there is something wrong with the JSON output? There seems to be some strange encoding. It's from a blog.
JSON:
[{
"title": "A visit to McSorley\u0027s Old Ale House",
"subtitle": "",
"summary": "\u0026lt;p\u0026gt;McSorley\u0026#39;s Ale House is Manhattan\u0026#39;s oldest pub\u0026lt;/p\u0026gt;"
}]
I successfully create NSData Object, but NSJSONSerialization fails, see the code:
func parseJSON(jsonString: String) -> [String: AnyObject]? {
guard let data: NSData = jsonString.dataUsingEncoding(NSUTF8StringEncoding)
else { return nil }
do {
let dictionary = try NSJSONSerialization.JSONObjectWithData(data, options: []) as? [String: AnyObject]
return dictionary
} catch {
print("JSON Error: \(error)")
return nil
}
}
Thanks!
Your code works fine the problem is your JSON file, try with another JSON file for example, the following one:
var json = "{\"xyz \":[{\"title\": \"\",\"subtitle\": \"\",\"summary\": \"\"}]}"
func parseJSON(jsonString: String) -> [String: AnyObject]? {
guard let data: NSData = jsonString.dataUsingEncoding(NSUTF8StringEncoding)
else { return nil }
do {
let dictionary = try NSJSONSerialization.JSONObjectWithData(data, options: []) as? [String: AnyObject]
return dictionary
} catch {
print("JSON Error: \(error)")
return nil
}
}
The output is:
Optional(["xyz": (
{
subtitle = "";
summary = "";
title = "";
}
)])
You're missing the key for the JSON dictionary, if for that I put it some key in the beginning of the JSON to make it works. Nevertheless I strongly recommend you use SwiftyJSON to parse JSON files in an excellent way.
Removing the HTML String
extension String {
/**
Strip the HTML tags for the string passed.
- parameter code: String to strip HTML.
- returns: The new string without HTML tags.
*/
func stripHtmlTags() -> String {
return self.stringByReplacingOccurrencesOfString("<[^>]+>", withString: "", options: .RegularExpressionSearch, range: nil)
}
}
I hope this help you.