Create ordered json string to use for key signing - json

To understand the problem I need to provide some background to what I am trying to achieve.
I am trying to create a JWT with this library that will be signed with a private key.
My problem is that the dictionary that I use to create the JSON is unordered, which therefore results in an unordered JSON string.
The code below prints the JSON string in any order.
let dictionary = ["aKey": "aValue", "anotherKey": "anotherValue"]
if let theJSONData = try? JSONSerialization.data(
withJSONObject: dictionary,
options: .prettyPrinted
),
let theJSONText = String(data: theJSONData,
encoding: String.Encoding.ascii) {
print("JSON string = \n\(theJSONText)")
}
let privateKey = theJSONText.data(using: .utf8)
let jwtSigner = JWTSigner.hs256(privateKey: privateKey)
let signedJWT = try myJWT.sign(using: jwtSigner) // This produces a JWT with an invalid signature
The result of this is that my JWT produces an invalid signature. How do I produce a JSON string that has order?

JSONEncoder encodes a dictionary always in the same order (which however might not be the given order of the dictionary). But maybe it fulfills your needs.
if let theJSONData = try? JSONEncoder().encode(dictionary) {
let theJSONText = String(data: theJSONData, encoding: .utf8)!
print("JSON string = \n\(theJSONText)")
}
And there is also the sortedKeys option of OutputFormatting
And – once again – never prettyPrint anything which is going to be sent to a server.

Related

add json string escape characters

let params = [["_event": "bulk-subscribe", "tzID":8, "message":"pid-1175152:"]]
let jParams = try! JSONSerialization.data(withJSONObject: params, options: [])
var jsonString:String = String.init(data: jParams, encoding: .utf8) ?? "err"
The result of the code is to get the following values
[{"_event":"bulk-subscribe","tzID":8,"message":"pid-1175152:"}]
The result I want is the following values. Result value with " added
["{"_event":"bulk-subscribe","tzID":8,"message":"pid-1175152:"}"]
What do I need to fix?
Thank you
Your requested output is an array containing one element, a serialized JSON dictionary.
You get this by creating params as a dictionary
let params : [String:Any] = ["_event": "bulk-subscribe", "tzID":8, "message":"pid-1175152:"]
and wrap the result of the serialization in square brackets. There are no escape characters involved.
let jParams = try! JSONSerialization.data(withJSONObject: params)
let jsonStringArray = [String(data: jParams, encoding: .utf8)!]

decode a json file that has a single unkeyed bool swift

there is a json file that returns 'true' or 'false' (Bool).
How can I parse it.
Json file doesn't have key. It returns only
true
or
false
JSONDecoder is overkill for this purpose.
The Data representation of JSON true is
let data = Data([0x74, 0x72, 0x75, 0x65])
To check it simply create a String and compare with "true"
let isRegistered = String(data: data, encoding: .utf8) == "true"
or compare the raw Data directly
let isRegistered = data == Data([0x74, 0x72, 0x75, 0x65])
While I would almost certainly do this the way vadian suggests, JSONDecoder can definitely decode a top-level Bool:
let data = Data("true".utf8)
let value = try JSONDecoder().decode(Bool.self, from: data)
There's no need for a container. The (very slight) advantage of decoding this way is that it's much easier to make it tolerant of small differences like extra whitespace (for example a trailing newline). It's also easier to detect invalid data (for example, an empty result or an error) and distinguish it from false.
You need let container = try decoder.singleValueContainer()
struct RegisteredModel: Codable{
var registered:Bool
init(from decoder: Decoder) throws {
let container = try decoder.singleValueContainer()
registered = try container.decode(Bool.self)
}
}
let json = "false"
let data = json.data(using: .utf8)!
do {
let result = try JSONDecoder().decode(RegisteredModel.self, from: data)
print(result)
}
catch {
print(error)
}

Strings fetched with JSON-API to be converted from base64 to UTF8 in Swift

I am developing an iOS App that fetches Trivia Questions from Open Trivia Database (API)
After reading the docs and played around with it I think that the best solution is to use base64 encoding (since it seems to be supported in Swift). I have successfully fetched the data and parsed it into structs using a JSONParser. The problem that I have to solve is how to convert the values from base64 to UTF8. (The keys are read correctly, and therefore it maps to my structs)
My first idea was to use decoder.dataDecodingStrategy = .base64, but that does not seem to have any effect at all. And I am not really sure why.
Is that the right way to do it, or should I decode it myself afterwards when the strings are read in to structs?
In short, the result of the Parsing is a struct containing a responseCode as an Int and array containing structs representing the questions with the strings that I want to convert to UTF8 as members
My code for parsing looks like this:
let urlPath = "https://opentdb.com/api.php?amount=10&encode=base64"
let apiURL = URL(string: urlPath)!
URLSession.shared.dataTask(with: apiURL) { (data, response, error) in
guard let data = data else {return}
do{
let decoder = JSONDecoder()
decoder.dataDecodingStrategy = .base64
let questionData = try decoder.decode(Response.self, from: data)
print(questionData)
}catch let err{
print("Error", err)
}
}.resume()
Base64 encoding is used for properties you declared as Data, not as Strings, like so:
struct Response: Codable {
let someBaseEncodedString: Data
var someString: String? {
get {
return String(data: someBaseEncodedString, encoding: .utf8)
}
}
}
So, for the example you are giving, all the properties that are returned as a base64 encoded string should have the Data type in your struct, and then after that decoded as strings.
As suggested by other answers, you can decode Data or Base-64 String after JSONSerialization or JSONDecoder decoded the API results.
But if you prefer to write decoding initializer, you can make it as follows:
This may not be much different from your own Response, I guess.
struct Response: Codable {
var responseCode: Int
var results: [Result]
enum CodingKeys: String, CodingKey {
case responseCode = "response_code"
case results
}
}
To prepare to write a decoding initializer for Response, I would like to use some extensions:
extension KeyedDecodingContainer {
func decodeBase64(forKey key: Key, encoding: String.Encoding) throws -> String {
guard let string = try self.decode(String.self, forKey: key).decodeBase64(encoding: encoding) else {
throw DecodingError.dataCorruptedError(forKey: key, in: self,
debugDescription: "Not a valid Base-64 representing UTF-8")
}
return string
}
func decodeBase64(forKey key: Key, encoding: String.Encoding) throws -> [String] {
var arrContainer = try self.nestedUnkeyedContainer(forKey: key)
var strings: [String] = []
while !arrContainer.isAtEnd {
guard let string = try arrContainer.decode(String.self).decodeBase64(encoding: encoding) else {
throw DecodingError.dataCorruptedError(forKey: key, in: self,
debugDescription: "Not a valid Base-64 representing UTF-8")
}
strings.append(string)
}
return strings
}
}
Using these extensions above, you can define the Result type as follows:
extension Response {
struct Result: Codable {
var category: String
var type: String
var difficulty: String
var question: String
var correctAnswer: String
var incorrectAnswers: [String]
enum CodingKeys: String, CodingKey {
case category
case type
case difficulty
case question
case correctAnswer = "correct_answer"
case incorrectAnswers = "incorrect_answers"
}
init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
self.category = try container.decodeBase64(forKey: .category, encoding: .utf8)
self.type = try container.decodeBase64(forKey: .type, encoding: .utf8)
self.difficulty = try container.decodeBase64(forKey: .difficulty, encoding: .utf8)
self.question = try container.decodeBase64(forKey: .question, encoding: .utf8)
self.correctAnswer = try container.decodeBase64(forKey: .correctAnswer, encoding: .utf8)
self.incorrectAnswers = try container.decodeBase64(forKey: .incorrectAnswers, encoding: .utf8)
}
}
}
(You have not mentioned if your Response (or other name?) is defined as a nested type or not, but I think you can rename or modify it yourself.)
With all things above, you can simply decode the API response as:
do {
let decoder = JSONDecoder()
let questionData = try decoder.decode(Response.self, from: data)
print(questionData)
} catch {
print("Error", error)
}
By the way, you say I think that the best solution is to use base64 encoding (since it seems to be supported in Swift), but is that really true?
Base-64 to Data is supported in JSONDecoder, but it is not what you expect. So, using another encoding can be a better choice.
But, anyway, JSON string can represent all unicode characters using only ASCII with \uXXXX or \uHHHH\uLLLL. So, I do not understand why the API designers do not provide an option Standard JSON Encoding. If you can contact to them, please tell them to provide the option, that may simplify many client side codes.

Conver FIRTimestamp to JSON

I'm having problems converting a document to Firebase, but I can not convert the FIRTimestamp data.
let json = try? JSONSerialization.data(withJSONObject: d.data(), options: .prettyPrinted)
Error
*** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: 'Invalid type in JSON write (FIRTimestamp)'
To remove FIRTimestamp from JSON
struct leadDocument: Codable {
let state: String
let details: String
}
let dataDescription = document.data() // your json response or value
var leadData = dataDescription
_ = leadData.removeValue(forKey: "serverTimeStamp") // remove FIRTimestamp
let requestData = try! JSONSerialization.data(withJSONObject: leadData, options: JSONSerialization.WritingOptions.prettyPrinted) as NSData?
let results = try JSONDecoder().decode(leadDocument.self, from: requestData! as Data)
OR covert FIRTimestamp to JSON
let db = Firestore.firestore()
let settings = db.settings
settings.areTimestampsInSnapshotsEnabled = true
db.settings = settings
let timestamp: Timestamp = document.get("serverTimeStamp") as! Timestamp
let date: Date = timestamp.dateValue()
print(date)
If you're trying to serialize the contents of a FIRTimestamp, you should either:
Convert it to a NSDate with dateValue, and serialize that instead
Convert it to seconds (and nanoseconds if desired) using the linked methods
When you deserialize those values, you may to convert them back into a FIRTimestamp with one of its constructors.

Creating a certain JSON data structure in Swift

For backend communication, my app requires a method to create a certainly structured JSON, and thats where i struggle.
The created JSON is supposed to look like this:
{
"data": {
"color":"yellow",
"size":"big"
}
}
Serializing a Dictionary with the required Data does not really seem to have the option to format the content properly, my best results look like this:
Optional({
Kategorie = Strassenschaeden;
PLZ = 46282;
Strasse = Erftweg;
Unterkategorie = Schlagloch;
})
I didnt find any helpful weblinks for my problem, and since im new to Swift and its documentation Im kinda stuck at the moment.
So my questions are:
Whats the preferred data structure for my JSON data (Dictionary/Array) and how do I create a JSON that is well-formated?
Thanks in advance :)
Edit: This is the interesting part of what i have used to achieve my "best result":
var data: [String: String] = ["Kategorie": "\(Kategorie)", "Unterkategorie": "\(Unterkategorie)", "Strasse": "\(Strasse)","PLZ": "\(PLZ)"]
self.post(data, url: "http://*************") { (succeeded: Bool, msg: String) -> () in
var alert = UIAlertView(title: "Success!", message: msg, delegate: nil, cancelButtonTitle: "Okay.")
func post(params : Dictionary<String, String>, url : String, postCompleted : (succeeded: Bool, msg: String) -> ()) {
var request = NSMutableURLRequest(URL: NSURL(string: url)!)
let JSONData:NSData = NSJSONSerialization.dataWithJSONObject(params, options: NSJSONWritingOptions.PrettyPrinted, error: &err)!
var json = NSJSONSerialization.JSONObjectWithData(JSONData, options: nil, error: &err) as? NSDictionary
println(json)
Here
let JSONData:NSData = NSJSONSerialization.dataWithJSONObject(params, options: NSJSONWritingOptions.PrettyPrinted, error: &err)!
var json = NSJSONSerialization.JSONObjectWithData(JSONData, options: nil, error: &err) as? NSDictionary
you are converting the params dictionary to JSON data – and then you convert the
JSON data back do a dictionary! What you probably want is to create a string
from the JSON data:
let jsonData = NSJSONSerialization.dataWithJSONObject(params, options: .PrettyPrinted, error: &err)!
let json = NSString(data: jsonData, encoding: NSUTF8StringEncoding)!
println(json)
Remarks:
Properties and variables should have names starting with lower case letters, e.g.
jsonData.
The explicit type annotation :NSData is not needed here, the Swift compiler can
infer the type automatically.
The option can be given as .PrettyPrinted instead of NSJSONWritingOptions.PrettyPrinted, the compiler infers the enumeration type
automatically.
Instead of forced unwrapping with ! you should use optional binding to check
for success.
Just an itch, that no one here recommend swiftyJSON for working with JSON in Swift.
Try it, you will lose your pain of dealing with JSON.
https://github.com/SwiftyJSON/SwiftyJSON
To Read JSON
let jsonData = jsonString.dataUsingEncoding(NSUTF8StringEncoding, allowLossyConversion: false)
let jsonObject = JSON(data: jsonData!)
To Write JSON
let jsonString = jsonObject.rawString()