i want to extract a word from a string something like this:
"{"photo" : "95.png",
"other_name" : "othername",
"name" : "Painting",
"services" : [],
"_id" : "id"}"
no i want to extract the value of name: from here how do i do that,
so it would be like any text the comes after "name" : " is the word i am looking for
i tried something like this
let index = onlyName.index(onlyName.index(of: "\"name\"") ?? onlyName.startIndex, offsetBy: 10)
let mySubstring = onlyName[..<index]
based on this question but onlyName.index(of: "\"name\"") is giving me null
i know i could just convert it to a json that will be easier but needs to be a string
so how can i get the value of the name,it could be using regx
"{"photo" : "95.png",
"other_name" : "othername",
"name" : "Painting",
"services" : [],
"_id" : "id"}"
The above is not the valid Json string, if it would be the valid Json string like the one below:
"{\"photo\" : \"95.png\",
\"other_name\" : \"othername\",
\"name\" : \"Painting\",
\"services\" : [],
\"_id\" : \"id\"}"
And if you can have valid Json string then you can easily get the value of any of the key i.e.
func dictionaryFromJsonString(_ json: String) -> [String: Any]? {
if let data = json.data(using: .utf8) {
do {
return try JSONSerialization.jsonObject(with: data, options: []) as? [String: Any]
} catch {
print(error.localizedDescription)
}
}
return nil
}
And you can use it as
let jsonString = "{\"photo\" : \"95.png\",
\"other_name\" : \"othername\",
\"name\" : \"Painting\",
\"services\" : [],
\"_id\" : \"id\"}"
if let dictionary = dictionaryFromJsonString(jsonString) {
print(dictionary["name"])
}
As some people above suggested, that string is probably json, and you have reference to it somewhere in code. In that case, you could simply convert it to data, decode it, and access name:
struct MyStruct: Codable {
let name: String
}
let string = "{\"photo\" : \"95.png\", \"other_name\" : \"othername\", \"name\" : \"Painting\", \"services\" : [], \"_id\" : \"id\"}"
if let data = string.data(using: .utf8),
let parsed = try? JSONDecoder().decode(MyStruct.self, from: data) {
print(parsed.name)
}
Related
I am really puzzled by this.
I have a class which initializes like this:
class MyClass {
let storage = Storage.sharedService
static let sharedInstance = MyClass()
fileprivate init() {
storage.dumpKeychain()
v1 = storage.loadNonimportantValue()
print("V1: \(v1 ?? "nil")")
v2 = storage.loadImportantValue()
print("V2: \(v2 ?? "nil")")
}
}
storage.dumpKeychain() is function (taken from internet) that prints content of the Keychain accessible to my app
func dumpKeychain() {
let query: [String: Any] = [
kSecClass as String : kSecClassGenericPassword,
kSecReturnData as String : kCFBooleanFalse!,
kSecReturnAttributes as String : kCFBooleanTrue!,
kSecReturnRef as String : kCFBooleanTrue!,
kSecMatchLimit as String: kSecMatchLimitAll
]
var result: AnyObject?
let lastResultCode = withUnsafeMutablePointer(to: &result) {
SecItemCopyMatching(query as CFDictionary, UnsafeMutablePointer($0))
}
var values = [String:String]()
if lastResultCode == noErr {
let array = result as? Array<Dictionary<String, Any>>
for item in array! {
if let key = item[kSecAttrAccount as String] as? String, let value = item[kSecValueData as String] as? Data {
values[key] = String(data: value, encoding:.utf8)
}
}
}
print(values)
}
And storage.loadImportantValue() is function that loads a value from given kSecAttrAccount ("Important")
fileprivate func loadImportantValue() -> String? {
var readQuery : [String : Any] = [
kSecClass as String: kSecClassGenericPassword,
kSecAttrAccount as String: "Important",
kSecReturnData as String: kCFBooleanTrue!
]
var storedData : AnyObject?
_ = SecItemCopyMatching(readQuery as CFDictionary, &storedData);
return String(data: storedData as? Data, encoding: String.Encoding.utf8)
}
What I see in the logs is that dumpKeychain returns:
[..., "Nonimportant": "correct value", "Important": "correct value", ...]
And the line print(value) in MyClass initializer prints:
V1: "correct value"
V2: "incorrect value"
How is it possible that these two calls made almost one after another return two different values from the same spot in Keychain for V2 and at the same time V1 is ok?
Thanks
Ok, I found the problem.
There really are two different values of the data.
This class is part of a framework which after updating was doing this. It is so that the prior version of framework was using another framework to manage keychain and that framework stored data with more attributes then my new version. And what I thought was replacing the data was actually adding new entry with less attributes.
So at the end after running the legacy code and then updating it with newer version there were two entries of the data at the "same" key.
How to convert json into dictionary for POST api call Swift
info contains an array of object that have User and Address keys You need
let dict1 : [String:Any] = ["ID" : "User123", "Name" : "Virat", "UserID" : 0]
let dict2 : [String:Any] = ["city" : "Delhi", "pin" : 123]
let addressDict : [String:Any] = ["User" : dict1,"Address" : dict2]
let infoDict : [String:Any] = ["info" :[addressDict]]
To better understand look to
// MARK: - Empty
struct Empty: Codable {
let info: [Info]
}
// MARK: - Info
struct Info: Codable {
let user: User
let address: Address
enum CodingKeys: String, CodingKey {
case user = "User"
case address = "Address"
}
}
// MARK: - Address
struct Address: Codable {
let city: String
let pin: Int
}
// MARK: - User
struct User: Codable {
let id, name: String
let userID: Int
enum CodingKeys: String, CodingKey {
case id = "ID"
case name = "Name"
case userID = "UserID"
}
}
You can also use the models above and convert the model to Data with JSONEncodable
The issue is that you're adding user and address as separate dictionaries on the array.
The JSON you posted has an array for info key but its ONE dictionary with two keys.
You need to combine the address and user dicts into one declaration and then wrap that in an array.
For ex:
let dict1 : [String:Any] = ["ID" : "User123", "Name" : "Virat", "UserID" : 0]
let dict2 : [String:Any] = ["city" : "Delhi", "pin" : 123]
let dicts : [String:Any] = ["User": dict1, "Address" : dict2]
let arr = [dicts]
let infoDict : [String:Any] = ["info" : arr]
EDIT: I would agree with SH_Khan that a much better way of doing this would be to create a Codable model
The issue is you are make dictionary per object.
try this code:
let userDict : [String:Any] = ["ID" : "User123", "Name" : "Virat", "UserID" : 0]
let addressDict : [String:Any] = ["city" : "Delhi", "pin" : 123]
let infoDict : [String:Any] = ["info" : ["User": addressDict, "Address": addressDict]]
Try this:
func getDictionaryFromString(_ json: String) -> [String: Any]? {
if let data = json.data(using: .utf8) {
do {
return try JSONSerialization.jsonObject(with: data, options: []) as? [String: Any]
} catch {
print(error.localizedDescription)
}
}
return nil
}
override func viewDidLoad() {
super.viewDidLoad()
let json = """
{"info": [{
"User": {"ID": "user123",
"Name": "Virat",
"UserID": 0
},
"Address": {"city": "Delhi",
"pin": 123
}
}
]
}
"""
if let dict = getDictionaryFromString(json) {
print(dict["info"] as! NSArray)
}
}
//Printed:
> (
> {
> Address = {
> city = Delhi;
> pin = 123;
> };
> User = {
> ID = user123;
> Name = Virat;
> UserID = 0;
> };
> } )
After JSONSerialization decoded and as! cast to NSDictionary get data below.
{
language = {
A00001 = {
chineseSimplified = CIMB;
chineseTraditional = CIMB;
english = CIMB;
japanese = CIMB;
korean = CIMB;
};
};
}
How to access into nested NSDictionary?
I'm able to get one layer data through data["language"], somehow I can't access multiple layers like:
data["language"]["A00001"]["english"]
data["language"]?["A00001"]?["english"]?
data["language"]!["A00001"]!["english"]!
Xcode returns this error:
Type 'Any' has no subscript members
Reference similar question:
How to access deeply nested dictionaries in Swift
Accessing Nested NSDictionary values in Swift 3.0
How do I manipulate nested dictionaries in Swift, e.g. JSON data?
Try casting it into [String: [String: [String: String]]], which is a nested structure for Swift Dictionary.
let json = """
{
"language": {
"A00001": {
"chineseSimplified" : "CIMB",
"chineseTraditional" : "CIMB",
"english" : "CIMB",
"japanese" : "CIMB",
"korean" : "CIMB"
}
}
}
"""
if let jsonD = json.data(using: String.Encoding.utf8) {
do {
if let data = try JSONSerialization.jsonObject(with: jsonD, options: JSONSerialization.ReadingOptions.mutableLeaves) as? [String: [String: [String: String]]] {
print(data["language"]!["A00001"]!["english"]!)
}
} catch let error {
print(error.localizedDescription)
}
}
Here is another answer using Swift 4's Codable API. I thinks it's worth trying if your code needs type safety and you are sure the fields in JSON are consistent.
let json = """
{
"language": {
"A00001": {
"chineseSimplified" : "CIMB",
"chineseTraditional" : "CIMB",
"english" : "CIMB",
"japanese" : "CIMB",
"korean" : "CIMB"
}
}
}
"""
struct MyLanguages:Codable {
struct LanguageGroup: Codable {
struct Language:Codable {
var chineseSimplified: String
var chineseTraditional: String
var english: String
var japanese: String
var korean: String
}
var A00001: Language
}
var language: LanguageGroup
}
if let jsonD = json.data(using: String.Encoding.utf8) {
let jsonDecoder = JSONDecoder()
do {
let myLang = try jsonDecoder.decode(MyLanguages.self, from: jsonD)
print(myLang.language.A00001.chineseSimplified)
}
catch let err {
print(err.localizedDescription)
}
}
On click of a submit button the data in my textfields and some other data are being converted to a json object like so…
let categoryName = self.categoryTextField.text
let categoryId = self.categoryID
let category_json: [String: [String:Any]] = [
"categoryDetails": [
"category_name": categoryName,
"category_id": categoryId
]
]
if let data = try? JSONSerialization.data(withJSONObject: category_json, options: .prettyPrinted),
let str = String(data: data, encoding: .utf8) {
print(str) // `str` gives the json object
self.categoryStrToPass = str
}
Now self.categoryStrToPass is assigned to another json object and then finally added to a string array like so…
let productID = self.prodID
let sellingPrice = self.mrpTextField.text
let categoryJSON = self.categoryStrToPass
let jsonObject: [String: [String:Any]] = [
"prodDetails": [
"product_id": productID,
"selling_price": sellingPrice,
“category_json”: categoryJSON
]
]
if let data = try? JSONSerialization.data(withJSONObject: jsonObject, options: .prettyPrinted),
let str = String(data: data, encoding: .utf8) {
print(str)
self.jsonStrToPass = str
self.jsonStringArray.append(self.jsonStrToPass)
}
Now I’m storing jsonStringArray to coredata like so…
_product?.setValue(self.jsonStringArray, forKey:
"productJsonArray") // productJsonArray is an attribute of type Transformable and Custom class type Array<String>
And it is being fetched like so...
if let jsonObjArr = result.value(forKey: "productJsonArray") as?
Array<NSString> {
print(jsonObjArr)
}
Now on 2 different instances I have submitted the data which means on printing jsonObjArr while fetching,it should have 2 different json objects in one array like so..
[{
"prodDetails" : {
"product_id" : "0",
"category_json" : "{\n \"categoryDetails\" : {\n \"category_id\" : \"0\",\n \"category_name\" : \"prodCAT\"\n }\n}",
"selling_price" : "500",
}
}
{
"prodDetails" : {
"product_id" : "1",
"category_json" : "{\n \"categoryDetails\" : {\n \"category_id\" : \"0\",\n \"category_name\" : \"CATNEW\"\n }\n}",
"selling_price" : "1000",
}
}]
But instead, printing jsonObjArr is giving this…in 2 different arrays like so...
[{
"prodDetails" : {
"product_id" : "0",
"category_json" : "{\n \"categoryDetails\" : {\n \"category_id\" : \"0\",\n \"category_name\" : \"prodCAT\"\n }\n}",
"selling_price" : "500",
}
}]
[{
"prodDetails" : {
"product_id" : "1",
"category_json" : "{\n \"categoryDetails\" : {\n \"category_id\" : \"0\",\n \"category_name\" : \"CATNEW\"\n }\n}",
"selling_price" : "1000",
}
}]
How can I get multiple json objects in one single array...?
you can add objects of type [String: Any] to array like so
let firstCategoryName = "first"
let firstCategoryId = 1
let firstCategory = [
"category_name": firstCategoryName,
"category_id": firstCategoryId
] as [String : Any]
let secondCategoryName = "second"
let secondCategoryId = 2
var category_json = [[String:Any]]()
category_json.append(firstCategory)
let secondCategory = [
"category_name": secondCategoryName,
"category_id": secondCategoryId
] as [String : Any]
category_json.append(secondCategory)
print(category_json)
then serialize the array
Swift 4.0:
let firstObj = ["prodDetails": [
"product_id": 5,
"selling_price": 6,
]]
let secondObj = ["prodDetails1": [
"product_id1": 5,
"selling_price1": 6,
]]
let jsonObject = jsonStringArray.addingObjects(from: [firstObj,secondObj])
if let data = try? JSONSerialization.data(withJSONObject: jsonObject, options: .prettyPrinted),
let str = String(data: data, encoding: .utf8) {
print(str) //prints array of dictionaries
}
category_json and jsonObject are of the same kind.
What you need to understand:
(NS)String <== String(data:encoding) or data(encoding:) ==> (NS)Data
Applied to specific String/Data: JSON:
JSON Stringified <== String(data:encoding) or data(encoding:) ==> JSON Data
Swift Array/ Swift (and the rest JSON compliant) <== (NS)JSONSerialization.jsonObject(withData:, options:) or (NS)JSONSerialization.data(withJSONObject:, options:) ==> JSON Data
You can't append like that the two JSON Stringified, you need to have an array at least at top level.
So, let's connect the dots, in pseudo code (not sure at all that the method name are error free)
let currentData = self.jsonStrToPass.data(encoding: .utf8)
let current = JSONSerialization.jsonObject(with:currentData, options:[]) as [String:[String:Any]]
let finalArray : [[String:[String:Any]]]()
finalArray.append(current)
finalArray.append(jsonObject)
let finalData = JSONSerialization.data(withJSONObject:finalArray, options:[])
let finalString = String(data:finalData, encoding:.utf8)
That's for the logic. I didn't do the if let, try/catch, etc.
I'd think it might be better to pass Any (for Swift Array/Dictionary instead of String) between your data to pass. It might be simpler to edit them (append, etc.) instead of String.
I have to deliver some specific JSON to a webserver, but I'm very confused about how to use the correct arrays and dictionaries, so I can't get to my final result. I'm starting from this:
["entries": [
"member" : [
"id" : key,
"name" : value
],
"timestamp" : "\(dateString)"
]
]
in the JSON file I have to send to the server I can only have 1 "entries" and several "member" with specific "id" and "name" and timestamp. I'm aware that I have to use dictionaries but can't get to this result. This is what I tried so fare:
var leden = [Int: String]()
var jsonDict = [String: Any]()
var dateString = Date()
for (key, value) in leden{
jsonDict["\(key)"] = ["member" : [
"id" : key,
"naam" : value
],
"timestamp" : "\(dateString)"
]
}
jsonFinaal = ["entries": jsonDict]
this is as close as I could get, but good enough because the key in jsonDict should not be there, I added the "entries" in a second dictionary to make this a unique entry.
Anyone who can do beter to help me to the solution?
The value for members is an array of dictionaries, not a dictionary
This creates the structure sorted by the id keys
let leden = [1 : "Foo", 2: "Bar"]
let sortedKeys = leden.keys.sorted()
let dateString = Date()
var members = [[String:Any]]()
for key in sortedKeys {
let naam = leden[key]!
members.append(["member" : ["id" : key, "naam" : naam], "timestamp" : "\(dateString)"])
}
let entries = ["entries": members]
print(entries)
And the JSON string with
do {
let jsonData = try JSONSerialization.data(withJSONObject: entries)
let json = String(data: jsonData, encoding: .utf8)
print(json)
} catch { print(error) }