for example I just want to convert this sample array into a JSON object
var test = [String : Any] ()
test["title"] = "title"
test["description"] = "description"
test["date"] = Date.init()
and I get this error:
use of unresolved identifier 'JSONEncoder'
print(JSONEncoder (test))
You are not using the encoder correctly. Try this
let encoder = JSONEncoder()
let json = try? encoder.encode(test)
Referencing app's document here, the only init method is like this, so you should not create the encoder itself to get you the JSON result.
init()
Creates a new, reusable JSON encoder with the default formatting
settings and encoding strategies.
Related
First off, what do we call a dictionary with a format like this in iOS?
(
{
name = "Apple";
value = "fruit-1";
},
{
name = "Banana";
value = "fruit-2";
}
)
And for my main question. I somehow need to format a string of JSON, like this:
[{"name":"Apple","value":"fruit-1"},{"name":"Banana","value":"fruit-2"}]
into whatever that format is called (of the string above).
For context, the existing approach of my project uses CoreData where the Server response (which uses the mystery format above) gets saved locally as a String, and I want to follow that format.
EDIT: for more context, I really need to just get the first format into the database because a module of a project was built to read the data with that format (e.g. make use of NSString.propertyList()).
Using a library called ios hierarchy viewer, I can see the saved object in the device.
Original format, server json to db (core data) in Objective-C:
What I've been trying to do in Swift, server json to local using JSONSerialization:
First off, what do we call a dictionary with a format like this in iOS?
According to the documentation of NSString.propertyList(), that's a "text representation of a property list".
It's a wonky, non-standard pretty-printing obtained by calling NSArray.description or NSDictionary.description.
Here's an example that shows a round-trip of data:
// The opening `{` indentation is fucky, but that's how it's generated.
let inputPropertyList = """
(
{
name = "Apple";
value = "fruit-1";
},
{
name = "Banana";
value = "fruit-2";
}
)
"""
// The result is an `Any` because we don't know if the root structure
// of the property list is an array or a dictionary
let deserialized: Any = inputPropertyList.propertyList()
// If you want the description in the same format, you need to cast to
// Foundation.NSArray or Foundation.NSDictionary.
// Swift.Array and Swift.Dictionary have a different description format.
let nsDict = deserialized as! NSArray
let roundTrippedPropertyList = nsDict.description
print(roundTrippedPropertyList)
assert(roundTrippedPropertyList == inputPropertyList)
The second format you show is what you get when you display an object in the debug console. That's the output of the object's description property. It isn't a "JSON string", exactly.
If you want to convert your objets to a true JSON string, see below.
As Alexander pointed out, the first string in your question is the output from NSString's propertyList() function. The format looks quite similar to "pretty-printed" JSON, but it's different enough that it it won't work that way.
The `propertyList() function is a debugging-only function, and I don't know of an existing way to parse that back into objects. If that is the string that's being sent by your server, your server is broken. If that's what you see in core data when you log the contents of a field, it's probably a misunderstanding on your part.
To convert an object to pretty JSON, see this answer, where I created an extension to the Encodable format that implements a property "prettyJSON":
extension Encodable {
var prettyJSON: String {
let encoder = JSONEncoder()
encoder.outputFormatting = .prettyPrinted
guard let data = try? encoder.encode(self),
let output = String(data: data, encoding: .utf8)
else { return "Error converting \(self) to JSON string" }
return output
}
}
That should work for any object that supports the Encodable protocol. (And your object should.)
Im stuck trying to model a JSON array which has no property name into my swift project with the goal of parsing the data and using it in my app. I know how to do this when there is a NAME for the array but I don't know how to make swift and this lackluster JSON understand each other. The path to the first "Company" in the JSON is "0.Company". The error I get is "Value of type 'WorkData' has no member '0'"
Im including pictures of my full project so it is easier to understand the structure of the code and what im trying to do. Please look at the picture for a clearer understanding I apologize if Im not explaining it well i'm new to programming.
import Foundation
class WorkData: Codable {
let WorkData: [WorkData]
let Company: String
let worklogDate: String
let issue: String
}
func parseData(jsonDataInput: Data) {
let decoder = JSONDecoder() // an object that decodes JSON data
do {
let decodedData = try decoder.decode(WorkData.self, from: jsonDataInput)
let Company = decodedData.0.Company
let worklogDate = decodedData.0.worklogDate
let issue = decodedData.0.issue
} catch {
print (error)
}
}
}
json
Trying to model JSON in Swift
Parsing JSON
You cannot start JSON with an array because JSON itself is an object {}
See example below:
{
"WorkData" : [
{"Company" : ""},
{"Company" : ""},
{"Company" : ""}
]
}
let decodedData = try decoder.decode(LocalizationsResponse.self, from: jsonDataInput)
decodedData will be an array
I’m coding Swift for iOS 11, and in some context (described in part 2) I end up with a variable of type String. However, when examining the variable in Xcode’s debugger the type is shown as _PFEncodedString (which I have read elsewhere is an internal subclass of String). For some reason (described below, but not directly related to the first part of my question) I want to transform the _PFEncodedString variable to a plain String variable. The only way I’ve found to do that is as follows:
var attributeName : String
// ... attributeName is assigned a value and ends up being _PFEncodedString
let data = attributeName.data(using: String.Encoding.utf8)!
let plainName = String(data: data, encoding: String.Encoding.utf8)! // this is type String not _PFEncodedString
The first part of my question is: Is this conversion safe or is there is simpler or better way to do it?
Part 2. For the second part of my question I will first describe the context where the above becomes an issue. I’m using Core Data and want to export attribute names (and values) to JSON. Using reflection I can find Core Data entity names and find the attribute names that should be exported to JSON. The problem is that when I use JSONEncoder with this data, the generated JSON contains Chinese characters whereas the Core Data model attribute names do not. I have found that JSONEncoder produces partly Chinese output when Strings in the exported object is of type _PFEncodedString, but works fine when handling "plain" String types. Therefore the first part of my questions.
Here is code that illustrates the problem, boiled down to a near minimum. QPTVideo is a subclass of NSManagedObject with two attributes ‘qaUUID’ and ‘thumbnailData’.
// First define a struct to be used with JSONEncoder
struct AttributeForJSON : Codable {
var name : String
}
let entityDescription = QPTVideo.entity()
let attributes = entityDescription.attributesByName // this is an array of (key: String, value: NSAttributeDescription).
var allAttributes : [AttributeForJSON] = [] // this will be an array of all the attribute names for export to JSON.
for attribute in attributes {
let attributeName = attribute.key // NOTE: debugger shows this as: attributeName String class name = _PFEncodedString
let aj = AttributeForJSON(name: attributeName)
print(aj.name) // this works just fine, printing “qaUUID” or “thumbnailData”
allAttributes.append(aj) // when exported, the attribute names look nothing like they should, containing Chinese characters.
// now use the work-around from first part of my question:
let data = attributeName.data(using: String.Encoding.utf8)!
let plainName = String(data: data, encoding: String.Encoding.utf8)! // NOTE: debugger shows this as: plainName String "thumbnailData"
let ajplain = AttributeForJSON(name: plainName)
allAttributes.append(ajplain) // when exported, the attribute names are correct.
}
// the rest is just use of JSONEncoder to generate the JSON
let encoder = JSONEncoder()
encoder.outputFormatting = .prettyPrinted
do {
let data = try encoder.encode(allAttributes)
try data.write(to: URL(fileURLWithPath: “/Users/someone/so.json"), options: Data.WritingOptions.atomic)
} catch {
print(error.localizedDescription)
}
The exported JSON looks like this (where I would have expected both of the first two name values to be "qaUUID", and the next two to be "thumbnailData"):
[
{
"name" : "慱啕䑉\u0000戨্"
},
{
"name" : "qaUUID"
},
{
"name" : "桴浵湢楡䑬瑡a\u0000戨্\u0001\u0000\u0000"
},
{
"name" : "thumbnailData"
}
]
So, my here question is: Why do I get Chinese characters? What am I doing wrong in how I use the Core Data reflection API and/or JSONEncoder? How do I do this without resorting to the work-around from the first part of my question?
In our iOS project, we are using SwiftyJSON and ObjectMapper to parse JSON responses and store them in models. However, for the models, we have to manually specify the mapping. eg; if I have a model class called User, which has name and age as properties, then while parsing, I have to specify the following in the User class:
func mapping(map: Map) {
name <- map["Name"]
age <- map["Age"]
}
Doing the same for all models is tedious and time consuming. Isn't there an approach to generalize the parsing? Like I pass any JSON to a function and specify the model, and the function should return me the model object with the parsed values, if they're available. I don't want to write a separate mapping for each model.
I think you should take a look at EVReflection, a Swift 3 library which does what you are looking for.
Update:
Swift 4 (now in Beta) seems to be bringing some changes which are relevant to this question.
struct MyStruct: Codable {
var str: String
var num: Int
}
let myStruct = MyStruct(str: "test", num:5)
let encoder = JSONEncoder()
let jsonData = try encoder.encode(myStruct)
let json = String(data: jsonData, encoding: .utf8)
print(json) // prints {"str": "test", "num": 5}
// Also decoding
let decoder = JSONDecoder()
let decoded = try decoder.decode(MyStruct.self, from: jsonData)
// decoded is a structure of type MyStruct
print(decoded.str) // test
The string to convert:
[{"description": "Hi","id":2,"img":"hi.png"},{"description": "pet","id":10,"img":"pet.png"},{"description": "Hello! :D","id":12,"img":"hello.png"}]
The code to convert the string:
var json = JSON(stringLiteral: stringJSON)
The string is converted to JSON and when I try to count how many blocks are inside this JSON (expected answer = 3), I get 0.
print(json.count)
Console Output: 0
What am I missing? Help is very appreciated.
Actually, there was a built-in function in SwifyJSON called parse
/**
Create a JSON from JSON string
- parameter string: Normal json string like '{"a":"b"}'
- returns: The created JSON
*/
public static func parse(string:String) -> JSON {
return string.dataUsingEncoding(NSUTF8StringEncoding)
.flatMap({JSON(data: $0)}) ?? JSON(NSNull())
}
Note that
var json = JSON.parse(stringJSON)
its now changed to
var json = JSON.init(parseJSON:stringJSON)
I fix it on this way.
I will use the variable "string" as the variable what contains the JSON.
1.
encode the sting with NSData like this
var encodedString : NSData = (string as NSString).dataUsingEncoding(NSUTF8StringEncoding)!
un-encode the string encoded (this may be sound a little bit weird hehehe):
var finalJSON = JSON(data: encodedString)
Then you can do whatever you like with this JSON.
Like get the number of sections in it (this was the real question) with
finalJSON.count or print(finalJSON[0]) or whatever you like to do.
There is a built-in parser in SwiftyJSON:
let json = JSON.init(parseJSON: responseString)
Don't forget to import SwiftyJSON!
I'm using as follows:
let yourString = NSMutableString()
let dataToConvert = yourString.data(using: String.Encoding.utf8.rawValue)
let json = JSON(data: dataToConvert!)
print("\nYour string: " + String(describing: json))
Swift4
let json = string.data(using: String.Encoding.utf8).flatMap({try? JSON(data: $0)}) ?? JSON(NSNull())