Swift: Cast Any or AnyObject to JSON - json

I want to wrap data to JSON before sending.
func request(content: [String]) {
try? JSONEncoder().encode(content)
This worked well when sending arrays of string. However, I would also like to send other structures that can be represented in JSON.
For example arrays containing arrays, or dictionaries.
My idea was to make it
func request(content: Any) {
try? JSONEncoder().encode(content)
Which gives Protocol 'Any' as a type cannot conform to 'Encodable'
How can I make the parameter as generic as possible so what can be formed as JSON gets formed as JSON and whatever fails, fails? I do understand I can't JSONify everything but I would like to be able to do it with things that I know of can be represented in JSON form. I found things like this https://stackoverflow.com/a/64471720/2161301 but apparently you can't do that on "normal" functions

You can use AnyCodable in your project.
Then you will be allowed to use [String: AnyEncodable] / [AnyEncodable] etc. in the places where you are not able to use Any while trying to use JSONEncoder api.

Related

Swift partially decode JSON and keep unparsed data

I'd like to decode a layered JSON protocol in parts. For example:
{
"eventType": "foo",
... other things
"eventData": <some json of unknown structure>
}
and have this decode into a Swift type like..
struct EventWrapper: Decodable {
eventType: String
... other things
eventData: String
}
where the eventData string contains exactly <some json of unknown structure>, i.e. the entire value of that property left in the original JSON encoded format. The intent would be to use information in this outer layer to decide where to send the inner layer. Once decided, only the inner layer data would be transferred and the recipient would understand its format and decode it further.
Some things I've tried that aren't quite what I want:
I can omit eventData entirely to parse just eventType and ... other things. But this option doesn't seem to allow easy access to the remaining unparsed data.
I can do a partial decode (as in #1) and then dispatch the original raw message to the next layer. This requires transferring unnecessary outer wrapper data and requires the next layer to understand and re-parse the outer layer.
With a custom init(from decoder: Decoder) method I can conditionally decode eventData to different types based on the value of eventType. But I can't seem to choose to not decode a particular property or access the un-decoded bytes.

Is it possible to deserialize data that looks like JSON (but isn't) using serde_json?

I have a hard time deserializing (using Rust's serde and serde_json v1.0) the following JSON I receive:
{
["string content"]
}
The object's array is not identified by a key, so the following doesn't work:
#[derive(Deserialize)]
struct Data {
key: Vec<String>
}
I've also tried using #[serde(flatten)] on the key field but I get an error:
can only flatten structs and maps (got a sequence)
The data I receive doesn't look like valid JSON. Is it still possible using serde_json?
The input you show is not valid JSON. You will not be able to use serde_json to deserialize that input because serde_json only accepts JSON.
If you find out what format the data is intended to be in, consider using (or writing) a Rust library dedicated to that specific format.

JSON into array regardless of depth

First question here so please bear with me on that one.
I'm trying to do a class that will handle http requests.
The server will definitely reply in JSON so - on top of handling requests - I'd like it to turn the JSON data into dictionaries as well.
As of now, I have this but I find it rather inconvenient ...
guard let json = try? JSONSerialization.jsonObject(with:data, options .mutableContainers) as? [String:Any] else { return }
I want this class to be useful in as many situations as possible. There will be situations in which the server will send 4 or 5d json.
My question is the following :
Can't I convert the json into an array, regardless of the depth it will have, php style?

Get the raw data of a JSON number using NSJSONSerializer

I'm trying to parse a JSON structure that looks like this:
{"amount": -9.45}
Using JSONSerializer, the amount gets parsed as a floating point number leading to errors in the numbers. This is the code I currently have:
import Foundation
let jsonData = "{\"amount\": -9.45}".data(using: .utf8)!
let jsonObject = try! JSONSerialization.jsonObject(with: jsonData, options: []) as! [String: Any]
jsonObject["amount"] // -9.449999999999999
Is there a way I can get to the underlying string that is being parsed into a floating point number so I can parse it myself?
Notes:
This is financial data so I'd like to parse it as either an Int or Decimal.
I cannot control the source of the data.
I'm trying to avoid pre-processing the data/writing my own JSON parser.
EDIT: After reviewing the code of the Swift version of JSONSerialization provided by Apple as part of the open source Foundation project, I do not believe that it's possible to solve this problem using the stock class. I made a patched version of the class that parses all numbers as Decimal's instead of Int's or Double's and that solved my problem.

Swift Vapor framework difference in JSON function calls

My question is what difference in Vapor JSON function calls return JSON(["foo":"bar"]) vs return try JSON(node: ["foo":"bar"])?
Both variants work, what is the right way?
Mixing them like return JSON(node: ["foo":"bar"]) or return try JSON(["foo":"bar"]) will make build fail.
import Vapor
let drop = Droplet()
drop.get("json") { req in
return JSON(["foo": "bar"])
}
drop.run()
I think I can answer this one. At first glance, these look pretty similar, but they're very different. The singular reason that everything comes back to in these two initializers is ... GENERICS.
No External Arg
No external arg initializer refers to JSON(["foo": "bar"]) above. We use these for non-failable initializers for types that can be directly represented in JSON. For example, [String: JSON], String, [JSON], Number(Int, UInt, Double), etc..
You may say, "wait a minute, I'm passing [String: String] above. Well, here's the thing ... actually we're not. JSON is ExpressibleAsStringLiteral so ["foo": "bar"] above actually becomes ["foo": JSON("bar")] and allows us to use the no-argument initializer.
With External Arg node:
We use the external argument to help the compiler disambiguate since we were unable to use the same external parameters for failable and non-failable initializers.
If you look through the node: initializer, it is a set of generic overloads that allow us to make things easier. As above we mentioned that we can pass ["foo": "bar"] directly because it converts to [String: JSON]. Well, if we have a type that is concretely [String: String] and we try to use JSON(stringDict) it'll fail. If however we use try JSON(node: stringDict) we can use generics to know that String is NodeRepresentible and we have enough context to properly convert it to JSON. Or at least try to!
By having the node: initializer, we can allow multiple different generic variants and work with multiple different types.
:)
Hope this clears some things up, this is a pretty nuanced area of the code base, happy to elaborate more.