I want to convert an array to string like this
let myArray:[Any] = ["element 1", "element 2", ["element 3.1", "element 3.2"], "element 4"]
to
let convertedString = "[\"element 1\",\"element 2\",\"[\\\"element 3.1\\\",\\\"element 3.2\\\"]\",\"element 4\"]"
I have tried this
do {
let jsonData: Data = try JSONSerialization.data(withJSONObject: myArray, options: [])
if let jsonString = NSString(data: jsonData, encoding: String.Encoding.utf8.rawValue) {
print(jsonString)
}
} catch let error as NSError {
print("Array convertIntoJSON - \(error.description)")
}
However I get this result
["element 1","element 2",["element 3.1","element 3.2"],"element 4"]
I have done this in java by using JSONArray. I just call toString method to do this in java. I need to get third element as a string like given example above.
This should do:
"\(myArray.map { "\($0)" })"
Related
I am sending a request to my vapor 1.5 server via Alamofire with body of type [String:Any] where Any type is a dictionary of String:String
When the request is received on the server I convert it to JSON type
guard let reqJson = request.json else {return}
How can I loop through this JSON object and convert it to an array of [String:Any]
The body of the request I send from the client app looks like this:
["DHSKL3920JFLSKXFgs":
["title": “some title”,
"body": “some body”,
"DHSKL3920JFLSKXFgs": "DHSKL3920JFLSKXFgs",
"unreadMessagesCount": 3],
"PKF993AVG59gkCM":
["title": “some title”,
"body": “some body”,
"DHSKL39": "DHSKL39",
"unreadMessagesCount": 3]]
You can use swift4 Codable and shrink your code to 4-5 lines. Documentation
If I understood you correctly. Maybe the following will help.
//This method uses high order function map
func convert(json:[String:Any]) -> [[String: Any]] {
let requiredObjects = json.map { $0.value as! [String:Any] } //force unwrapping
return requiredObjects
}
//This method uses simple loop
func convert(json:[String:Any]) -> [[String: Any]] {
var requiredObjects = [[String:Any]]()
for (key, value) in json.enumerated() {
requiredObjects.append([value.key : value.value])
}
return requiredObjects
}
struct DataFromClientSendNotifications {
let title: String
let body: String
let sound: String
let badge: String
let fcmToken: String
let unreadMessagesCount: String
}
guard let reqJson = request.json else {
throw Abort.custom(status: .badRequest, message: message)
}
for obj in reqJson.object! {
print("new obj is \(obj)")
let objKey = obj.key
let objValue = obj.value.object
print("objectValue here is \(objValue)")
let title = objValue?["title"]?.string
let body = objValue?["body"]?.string
let unreadMessagesCount = objValue?["unreadMessagesCount"]?.string
let sound = objValue?["sound"]?.string
let badge = objValue?["badge"]?.string
let fcmToken = objValue?["objValue"]?.string
let itemCompleted = DataFromClientSendNotifications(title: title!, body: body!, sound: sound!, badge: badge!, fcmToken: fcmToken!, unreadMessagesCount: unreadMessagesCount!)
print("itemCompleted is \(itemCompleted)")
//now you can do whatever you want with itemCompleted
}
I'm using an API that returns this pretty horrible JSON:
[
"A string",
[
"A string",
"A string",
"A string",
"A string",
…
]
]
I'm trying to decode the nested array using JSONDecoder, but it doesn't have a single key and I really don't know where to start… Do you have any idea?
Thanks a lot!
If the structure stays the same, you can use this Decodable approach.
First create a decodable Model like this:
struct MyModel: Decodable {
let firstString: String
let stringArray: [String]
init(from decoder: Decoder) throws {
var container = try decoder.unkeyedContainer()
firstString = try container.decode(String.self)
stringArray = try container.decode([String].self)
}
}
Or if you really want to keep the JSON's structure, like this:
struct MyModel: Decodable {
let array: [Any]
init(from decoder: Decoder) throws {
var container = try decoder.unkeyedContainer()
let firstString = try container.decode(String.self)
let stringArray = try container.decode([String].self)
array = [firstString, stringArray]
}
}
And use it like this
let jsonString = """
["A string1", ["A string2", "A string3", "A string4", "A string5"]]
"""
if let jsonData = jsonString.data(using: .utf8) {
let myModel = try? JSONDecoder().decode(MyModel.self, from: jsonData)
}
This is a bit interesting for decoding.
You don't have any key. So it eliminates the need of a wrapper struct.
But look at the inner types. You get mixture of String and [String] types. So you need something that deals with this mixture type. You would need an enum to be precise.
// I've provided the Encodable & Decodable both with Codable for clarity. You obviously can omit the implementation for Encodable
enum StringOrArrayType: Codable {
case string(String)
case array([String])
init(from decoder: Decoder) throws {
let container = try decoder.singleValueContainer()
do {
self = try .string(container.decode(String.self))
} catch DecodingError.typeMismatch {
do {
self = try .array(container.decode([String].self))
} catch DecodingError.typeMismatch {
throw DecodingError.typeMismatch(StringOrArrayType.self, DecodingError.Context(codingPath: decoder.codingPath, debugDescription: "Encoded payload conflicts with expected type"))
}
}
}
func encode(to encoder: Encoder) throws {
var container = encoder.singleValueContainer()
switch self {
case .string(let string):
try container.encode(string)
case .array(let array):
try container.encode(array)
}
}
}
Decoding Process:
let json = """
[
"A string",
[
"A string",
"A string",
"A string",
"A string"
]
]
""".data(using: .utf8)!
do {
let response = try JSONDecoder().decode([StringOrArrayType].self, from: json)
// Here, you have your Array
print(response) // ["A string", ["A string", "A string", "A string", "A string"]]
// If you want to get elements from this Array, you might do something like below
response.forEach({ (element) in
if case .string(let string) = element {
print(string) // "A string"
}
if case .array(let array) = element {
print(array) // ["A string", "A string", "A string", "A string"]
}
})
} catch {
print(error)
}
A possible solution is to use the JSONSerialization, then you might simply dig inside such json, doing so:
import Foundation
let jsonString = "[\"A string\",[\"A string\",\"A string\", \"A string\", \"A string\"]]"
if let jsonData = jsonString.data(using: .utf8) {
if let jsonArray = try JSONSerialization.jsonObject(with: jsonData, options: []) as? [Any] {
jsonArray.forEach {
if let innerArray = $0 as? [Any] {
print(innerArray) // this is the stuff you need
}
}
}
}
This question already has an answer here:
JSON with "(" ")" brackets istead of "[" "]" [closed]
(1 answer)
Closed 5 years ago.
When I add an array in my JSON object and convert it to JSON, after printing there is parenthesis instead of curly brace.
var jsonObject : [String: Any] = [:]
var x = [1,2,3,4]
jsonObject["arr"] = x
do{
let jsonData = try JSONSerialization.data(withJSONObject: jsonObject, options: [])
let decoded = try JSONSerialization.jsonObject(with: jsonData, options: [])
print(decoded)
} ...
And the result of printing:
{
arr = (
1,
2,
3,
4
);
}
But in JSON, to show an array it uses [ instead of (.
What's wrong?
You are converting the dictionary(!) to JSON and then you convert it back and print it.
To print the JSON string you have to write
let jsonData = try JSONSerialization.data(withJSONObject: jsonObject)
let jsonString = String(data: jsonData, encoding: .utf8)
print(jsonString ?? "Wrong data")
When you print a Swift object, Swift debugger is free to print it however it wants. It's just for convenience, there are no guarantees about the format. So, it prints arrays in parenthesis.
When you convert it to JSON, on the other hand, it creates a JSON string, and JSON has a specified format for arrays, which includes square brackets.
UPDATE: after reading the question again, it seems to me you're under the impression that you're printing a JSON representation of your object. You're not. What you do currently is you convert your Swift object to JSON, and then convert it back to a Swift object. And then you print the resulting Swift object (see paragraph 1 of my answer).
So, if your goal is to print the JSON representation, you need to convert jsonData to String (see other answers), and print that string. You shouldn't use NSJSONSerialization again for backward conversion.
You should use this to print your JSON.
public extension Collection {
/// Convert self to JSON String.
/// - Returns: Returns the JSON as String or empty string if error while parsing.
func json() -> String {
do {
let jsonData = try JSONSerialization.data(withJSONObject: self, options: [.prettyPrinted])
guard let jsonString = String(data: jsonData, encoding: String.Encoding.utf8) else {
print("Error creating string with data.")
return "{}"
}
return jsonString
} catch let parseError {
print("json serialization error: \(parseError)")
return "{}"
}
}
}
The conversion is correct, It's just that you are printing decoded version, which is swift dictionary, not JSON.
var jsonObject : [String: Any] = [:]
var x = [1,2,3,4]
jsonObject["arr"] = x
print(jsonObject.json())
do{
let jsonData = try JSONSerialization.data(withJSONObject: jsonObject, options: [])
let decoded = try JSONSerialization.jsonObject(with: jsonData, options: [])
print(decoded)
}
Output:-
{
"arr" : [
1,
2,
3,
4
]
}
I added some print clauses to you code, and pasted the console log as comments.
var jsonObject : [String: Any] = [:]
var x = [1,2,3,4]
jsonObject["arr"] = x
print(type(of: jsonObject)) // Dictionary<String, Any>
do{
let jsonData = try JSONSerialization.data(withJSONObject: jsonObject, options: .prettyPrinted)
let decoded = try JSONSerialization.jsonObject(with: jsonData, options: .allowFragments)
print(type(of: decoded)) // __NSSingleEntryDictionaryI
let str = String(data: jsonData, encoding: .utf8)
print(str!) /* {
"arr" : [
1,
2,
3,
4
]
}*/
}
As you can see, the type of decoded is __NSSingleEntryDictionaryI, so its result of printing is not as what you expected, with "(" instead of "[". If you want the right representation in JSON, you should convert the jsonData to String.
I created a struct and want to save it as a JSON-file.
struct Sentence {
var sentence = ""
var lang = ""
}
var s = Sentence()
s.sentence = "Hello world"
s.lang = "en"
print(s)
...which results in:
Sentence(sentence: "Hello world", lang: "en")
But how can I convert the struct object to something like:
{
"sentence": "Hello world",
"lang": "en"
}
Swift 4 introduces the Codable protocol which provides a very convenient way to encode and decode custom structs.
struct Sentence : Codable {
let sentence : String
let lang : String
}
let sentences = [Sentence(sentence: "Hello world", lang: "en"),
Sentence(sentence: "Hallo Welt", lang: "de")]
do {
let jsonData = try JSONEncoder().encode(sentences)
let jsonString = String(data: jsonData, encoding: .utf8)!
print(jsonString) // [{"sentence":"Hello world","lang":"en"},{"sentence":"Hallo Welt","lang":"de"}]
// and decode it back
let decodedSentences = try JSONDecoder().decode([Sentence].self, from: jsonData)
print(decodedSentences)
} catch { print(error) }
Swift 4 supports the Encodable protocol e.g.
struct Sentence: Encodable {
var sentence: String?
var lang: String?
}
let sentence = Sentence(sentence: "Hello world", lang: "en")
Now you can automatically convert your Struct into JSON using a JSONEncoder:
let jsonEncoder = JSONEncoder()
let jsonData = try jsonEncoder.encode(sentence)
Print it out:
let jsonString = String(data: jsonData, encoding: .utf8)
print(jsonString)
{
"sentence": "Hello world",
"lang": "en"
}
https://developer.apple.com/documentation/foundation/archives_and_serialization/encoding_and_decoding_custom_types
Use the NSJSONSerialization class.
Using this for reference, you may need to create a function which returns the JSON serialized string. In this function you could take the required properties and create a NSDictionary from them and use the class mentioned above.
Something like this:
struct Sentence {
var sentence = ""
var lang = ""
func toJSON() -> String? {
let props = ["Sentence": self.sentence, "lang": lang]
do {
let jsonData = try NSJSONSerialization.dataWithJSONObject(props,
options: .PrettyPrinted)
return String(data: jsonData, encoding: NSUTF8StringEncoding)
} catch let error {
print("error converting to json: \(error)")
return nil
}
}
}
Because your struct only has two properties it might be easier to just build the JSON string yourself.
Here's a nice extension and a method for JSON encoding/decoding:
extension Encodable {
func toJSONString() -> String {
let jsonData = try! JSONEncoder().encode(self)
return String(data: jsonData, encoding: .utf8)!
}
}
func instantiate<T: Decodable>(jsonString: String) -> T? {
return try? JSONDecoder().decode(T.self, from: jsonString.data(using: .utf8)!)
}
Sample usage:
struct Sentence: Codable {
var sentence = ""
var lang = ""
}
let sentence = Sentence(sentence: "Hello world", lang: "en")
let jsonStr = sentence.toJSONString()
print(jsonStr) // prints {"lang":"en","sentence":"Hello world"}
let sentenceFromJSON: Sentence? = instantiate(jsonString: jsonStr)
print(sentenceFromJSON!) // same as original sentence
In my tests I'm used to write Strings in arrays in different lines like
let jsonString = ["{"
,"\"url\": \"http://localhost:8090/rest/api/3\","
, "\"id\": \"3\","
, "\"description\": \"A test that needs to be done.\","
, "\"name\": \"Test\","
, "\"subtest\": false,"
, "\"avatar\": 1"
,"}"].reduce("", combine: +)
That works fine, still my array get 145 lines for a large test json string.
With 145 lines (or maybe less, didn't tried it line by line) the build task hangs while "Compiling Swift source files".
First, that is a bit crazy. 30 lines are ok, 145 not? What?
Second, what is a better solution to write a String in multiple lines in Swift? - I don't want to load a json and parse it from an external file.
This is probably because of type inference.
Try giving an explicit [String] type to an array variable to help the compiler figure it out, then apply reduce to the variable.
let arrayOfStrings: [String] = ["{"
,"\"url\": \"http://localhost:8090/rest/api/3\","
, "\"id\": \"3\","
, "\"description\": \"A test that needs to be done.\","
, "\"name\": \"Test\","
, "\"subtest\": false,"
, "\"avatar\": 1"
,"}"] // etc
let jsonString = arrayOfStrings.reduce("", combine: +)
Anyway, to create JSON you should make a dictionary then serialize it with NSJSONSerialization, this is less error prone:
Swift 2
do {
// Create a dictionary.
let dict = ["url": "http://localhost:8090/rest/api/3", "id": "3"] // etc
// Encode it to JSON data.
let jsonData = try NSJSONSerialization.dataWithJSONObject(dict, options: [])
// Get the object back.
if let jsonObject = try NSJSONSerialization.JSONObjectWithData(jsonData, options: []) as? [String:String] {
print(jsonObject)
}
// Get it as a String.
if let jsonString = String(data: jsonData, encoding: NSUTF8StringEncoding) {
print(jsonString)
}
} catch let error as NSError {
print(error)
}
Swift 3
do {
// Create a dictionary.
let dict = ["url": "http://localhost:8090/rest/api/3", "id": "3"] // etc
// Encode it to JSON data.
let jsonData = try JSONSerialization.data(withJSONObject: dict, options: [])
// Get the object back.
if let jsonObject = try JSONSerialization.jsonObject(with: jsonData, options: []) as? [String:String] {
print(jsonObject)
}
// Get it as a String.
if let jsonString = String(data: jsonData, encoding: String.Encoding.utf8) {
print(jsonString)
}
} catch let error as NSError {
print(error)
}