I get json from server and I need to convert id field to string in swift code.
The problem is json sometimes returns "12345", sometimes returns 12345 (with or without quotes).
Is it possible to resolve this issue without of checking the value type and checking if the conversion result is nil?
UPDATED
Example of code I use with checking conversion result:
let result = (some_index as? String) ?? String(some_index as! Int)
The problem is in objective-C you have [NSString stringWithFormat:#"%#", some_object]. But in swift you have optionals and it tries to insert word "optional" into result.
UPDATED
STOP spam with random answers about optionals. The question is concrete - "how to simply unwrap json value which may look like String, Int or doesn't exist at all?"
swift How to remove optional String Character
In this question they ask how to convert Int? -> Int, String? -> String and similar. In my case I don't know if I have Int? or String? as the initial type.
Both Stringand Int conform to CustomStringConvertible, so you could optional downcast the value to CustomStringConvertible and use String Interpolation
let dict : [String:Any] = ["Foo" : 12345]
if let value = dict["Foo"] as? CustomStringConvertible {
let result = "\(value)"
}
And blame the owner of the web service for sending inconsistent data ;-)
Related
I'm trying to encode/decode JSON with Swift's JSONEncoder/Decoder. My JSON contains a dictionary with UUID as Key. If this dictionary is empty, Swift fails with
Expected to decode Array<Any> but found a dictionary instead.
While analyzing I noticed, that Swift creates different repressions of the empty dictionary depending on the key's type. The following minimum example illustrates the problem quite well:
import Foundation
typealias Key = UUID // or String
struct Foo: Codable {
let data: [Key: String]
}
let foo = Foo(data: [:])
let encodedData = try JSONEncoder().encode(foo)
let foo2 = try JSONDecoder().decode(Foo.self, from: encodedData)
print(String(decoding: encodedData, as: UTF8.self))
When using UUID as key's type you get:
{"data":[]}
and when using String as key's type you get:
{"data":{}}
I'd expect {"data":{}} in all cases. What I'm doing wrong here?
Closed. This question needs to be more focused. It is not currently accepting answers.
Want to improve this question? Update the question so it focuses on one problem only by editing this post.
Closed 4 years ago.
Improve this question
The new Swift "Decoder" class sounds like a great way to parse JSON data, but all of the examples I've found use a well-known, well-defined 'struct' to do so.
In my case I'm querying an arbitrary website that returns a HUGE JSON string and I only care about a few of the (deeply nested) fields, so I don't want to take all that time to define a 'struct' to get at them.
Is it even possible to do this with "Decoder"? And if so, how does one go about it?
The question seems to be based on a misapprehension about how Decodable works. As a convenience, Decodable is willing to do some automatic code generation behind the scenes so that you can define a struct or nest of structs and just decode the entirety of the JSON. But you are not required to take advantage of that in order to decode JSON.
There is no need to define struct properties for "fields" you don't care about. If a JSON dictionary contains 100 keys and your corresponding struct contains just one property, no problem; that key will be fetched, and no others.
With regard to the "deeply nested" part, it should not take you much time to write simple nested structs that perform the dive to reach the dictionary you really care about. But if you don't want to do even that, you could write an implementation of init(from:) that dives down and fetches out the desired values.
In other words, if you think of Decodable as consisting primarily of your implementation of init(from:), and learn to write the code that it needs, you will see that this JSON can be parsed in a few quick simple lines of code.
As an example, here's a JSON sketch of a deeply nested piece of information with a bunch of extra information at every level that we want to ignore:
{
"ignore": true,
"outer1": {
"ignore": true,
"outer2": {
"ignore": true,
"outer3": {
"name": "matt",
"ignore": true
}
}
}
}
What I'd like to do is define a very simple struct Person that consists solely of the deeply nested name:
struct Person : Decodable {
let name : String
}
I can do that! To do so, I implement Decodable myself, supplying a "hoover" CodingKey adopter struct and an implementation of init(from:), like this (this may look like a lot of work, but it isn't, because the AnyCodingKey implementation is boilerplate, copied and pasted from here, and the init(coder:) implementation is just a few lines of code that were easy to write):
struct Person : Decodable {
let name : String
struct AnyCodingKey : CodingKey {
var stringValue: String
var intValue: Int?
init(_ codingKey: CodingKey) {
self.stringValue = codingKey.stringValue
self.intValue = codingKey.intValue
}
init(stringValue: String) {
self.stringValue = stringValue
self.intValue = nil
}
init(intValue: Int) {
self.stringValue = String(intValue)
self.intValue = intValue
}
}
init(from decoder: Decoder) throws {
var con = try! decoder.container(keyedBy: AnyCodingKey.self)
con = try! con.nestedContainer(keyedBy: AnyCodingKey.self, forKey: AnyCodingKey(stringValue:"outer1"))
con = try! con.nestedContainer(keyedBy: AnyCodingKey.self, forKey: AnyCodingKey(stringValue:"outer2"))
con = try! con.nestedContainer(keyedBy: AnyCodingKey.self, forKey: AnyCodingKey(stringValue:"outer3"))
let name = try! con.decode(String.self, forKey: AnyCodingKey(stringValue:"name"))
self.name = name
}
}
When I want to dive into the JSON and grab the name information, it's trivial:
let person = try! JSONDecoder().decode(Person.self, from: json)
The result is a Person object with name value "matt". Note that I didn't have to add any of the ignore keys and I didn't need to make a nest of structs.
Sure you can achieve this but with both JSonSerialization & Decodable , you have to serialize the json until reach the desired content then decode it ,but instead I recommend to create structs for root keys only then decode , think of it as it's a path from top to bottom don't decode a key that isn't in the path of your desired content
In an iPhone app using SWIFT, I'm having to deal with a third-party API that sends a escaped string instead of a JSON object as response.
The response looks like this:
"[
{
\"ID\":3880,
\"Name\":\"Exploration And Production Inc.\",
\"ContractNumber\":\"123-123\",
\"Location\":\"Booker #1\",
\"Volume\":1225.75,
\"OtherFees\":10.0
}
]"
Up until now I have been dealing with this by manipulating the string to remove the unwanted characters until I get a JSON-like string and then parsing that as usual.
Angular has a handy function to deal with this:
angular.fromJson(response.data);
Java has its own way to deal with it. Is an equivalent function in Swift?
If you are parsing it to a dictionary then the simplest solution will be to convert String to Data and use JSONSerialization:
if let data = string.data(using: .utf8) {
do {
let responseArray = try JSONSerialization.jsonObject(with: data, options: []) as? [[String: Any]]
print(responseArray)
}
catch {
print(error)
}
}
But ofcourse it would be better to process it as a Codable model in which case its again just as simple as:
try JSONDecoder().decode([Item].self, from: data)
Provided that you have a valid Decodable model like so:
struct Item: Decodable {
let id: Int
let name: String
//add the other keys you want to use (note its type sensitive)
enum CodingKeys: String, CodingKey {
case id = "ID"
case name = "Name"
}
}
Lastly, avoid stringified jsons because its an easy source of errors.
Malformed strings or small/large deviations in the structure can go easily unnoticed.
Let the backend team know that they should follow a protocol in their API that its consumers can rely on.
By setting the json format, its essentially like a contract that showcases its content and purpose with clarity.
Sending a stringified json is simply lazy and reflects poorly on its designer, imho.
I am receiving JSON from an API and the response can be one of 30 types. Each type has a unique set of fields, but all responses have a field type which states which type it is.
My approach is to use serde. I create a struct for each response type and make them decodable. Once I have that how do I choose which struct should be used for a freshly received message?
At the moment, I've created another struct TypeStruct with only a single field for type. I decode the JSON into a TypeStruct, then choose the appropriate struct for received message, based on type value, and decode the message again.
I would like to get rid of this decoding duplication.
You can use the existing enum deserialization. I'll give a step by step example to deserialize your format to the following enum:
#[derive(Debug, PartialEq, Eq, Deserialize)]
enum MyType {
A {gar: ()},
B {test: i32},
C {blub: String},
}
Start with an example json string:
let json = r#"{"type": "B", "test": 42}"#;
Turn it into a Value enum
let mut json: serde_json::Value = serde_json::from_str(json).unwrap();
Rip out the type field
let type_ = {
let obj = json.as_object_mut().expect("object");
let type_ = obj.remove("type").expect("`type` field");
if let serde_json::Value::String(s) = type_ {
s
} else {
panic!("type field not a string");
}
};
Create the "proper" enum json. A struct with a single field where the name of the field is the enum variant and the value of the field is the variant value
let mut enum_obj = std::collections::BTreeMap::new();
enum_obj.insert(type_, json);
let json = serde_json::Value::Object(enum_obj);
Use the generated json deserializer to turn the json into a value of your enum
let obj: MyType = serde_json::from_value(json).unwrap();
Im trying to parse json in weather app, but have hit a snag that i cannot get past.
I do get an error, "Type 'int' does not conform to Protocol 'StringLiteralConvertible'" in the following code.
Ive tried casting the jsonResult["main"] but that does instead give the error "Operand of postfix should have optional type, type is AnyObject". Do i need to downcast the Array in some way and how, if so, should i do that?
I´ve searched so much for this but could not find any help in other posts. Code as follows.
func updateWeatherInfo(latitude: CLLocationDegrees, longitude: CLLocationDegrees) {
Alamofire.request(.GET, AlongRequest)
.responseJSON { (_, _, JSON, error) in
println(JSON)
self.updateUISuccess(JSON as NSArray!)
}
}
func updateUISuccess(jsonResult: NSArray) {
self.loading.text = nil
self.loadingIndicator.hidden = true
self.loadingIndicator.stopAnimating()
if let tempResult = ((jsonResult["main"] as NSArray)["temp"] as? Double)
This would be easier to give a definitive answer to if you provide the JSON that you're trying to parse, but the error message you're getting is clear.
That error is because you're trying to access what you've declared as an NSArray instance with a string subscript, twice in this one line:
if let tempResult = ((jsonResult["main"] as NSArray)["temp"] as? Double)
jsonResult is declared as an NSArray parameter, and then you're casting jsonResult["main"] to NSArray before trying to subscript it with ["temp"]. The problem here is that NSArray (and built-in Swift arrays) only use integer-based subscripting. The error is saying that where the Swift compiler is expecting an Int, you've provided a string literal.
To fix this, you'll need to go in one of two directions. If the structure you're trying to access actually has these string keys, then you should be using NSDictionary instead of NSArray in both cases. If not, and it's an integer-index array, you should be using integers.