Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Cant Parse Dynamic object like array string and dictonary #317

Closed
maulikshah09 opened this issue Jun 7, 2020 · 16 comments
Closed

Cant Parse Dynamic object like array string and dictonary #317

maulikshah09 opened this issue Jun 7, 2020 · 16 comments

Comments

@maulikshah09
Copy link

maulikshah09 commented Jun 7, 2020

Language : Swift
version : 5.0

Response from server

{
    "status": "success",
    "code": 200,
    "message": "Data received successfully",
    "data": [
        {
            "PId": 1,
            "PharmacyName": "Xyz",
            "ContactPerson": "test",
            "EmailId": "ZYZ@gmail.com",
            "PhoneNumer": "1234567890",
            "Address1": "test",
            "Address2": "test",
            "City": "test",
            "EntryDate": "2020-06-06T11:05:59.42",
            "ModifyDate": "2020-06-06T11:05:59.42",
            "UserId": 1,
            "IsDelete": false
        },
        {
            "PId": 2,
            "PharmacyName": "ZZZ",
            "ContactPerson": "ZZZ",
            "EmailId": "ZZz@gmail.com",
            "PhoneNumer": "1234567890",
            "Address1": "test",
            "Address2": "test",
            "City": "test",
            "EntryDate": "2020-06-06T11:05:32.967",
            "ModifyDate": "2020-06-06T11:05:32.967",
            "UserId": 1,
            "IsDelete": false
        }
    ]
}

I have a the below model class

I have below model

**Code **

protocol arrayorDictonary { }

extension Array: arrayorDictonary { }
extension Dictionary: arrayorDictonary { }


//empty model
class EmptyModelWithData : EVObject {
    var message = ""
    var status  = ""
    var code = 0
    var data : arrayorDictonary?
}
Alamofire.request(baseURL + urlMethod , method: method ,parameters: nil, encoding: JSONEncoding.default, headers:headerDic as? HTTPHeaders).responseObject {(response : DataResponse<EmptyModelWithData>) in
            SVProgressHUD.dismiss()
            let statusCode = response.response?.statusCode
            if response.result.value != nil {
             }
  }

and i get the following warning: while doing this

WARNING: The class 'EmptyModelWithData' is not key value coding-compliant for the key 'data'
There is no support for optional type, array of optionals or enum properties.
As a workaround you can implement the function 'setValue forUndefinedKey' for this. See the unit tests for more information

Note: Data is not specific type. some time data is array. some time data is dictionary.
and sometime it would be string. so i create protocol but not working

I hope I get solution from here..

Thank you so much..

@maulikshah09 maulikshah09 changed the title Cant Parse Dynamic object Cant Parse Dynamic object like array string and dictonary Jun 9, 2020
@evermeer
Copy link
Owner

evermeer commented Jun 9, 2020

your data property should be an array of objects like this:
var data : [Pharmacy]?
And your Pharmacy object would look like:

class Pharmacy: EVObject {
   var Pid: NSNumber?
   var PharmacyName: String?
   var ContactPerson: String?
  //rest of vars
}

Since there is support for single object to array this should work if you only get 1 pharmacy as an object instead of an array

@maulikshah09
Copy link
Author

maulikshah09 commented Jun 9, 2020

your data property should be an array of objects like this:
var data : [Pharmacy]?
And your Pharmacy object would look like:

class Pharmacy: EVObject {
   var Pid: NSNumber?
   var PharmacyName: String?
   var ContactPerson: String?
  //rest of vars
}

Since there is support for single object to array this should work if you only get 1 pharmacy as an object instead of an array

Sir my data variable is not fixed...sometime it's array..sometime it's string or sometime it's dictionary..when i work on codable decodable it's working fine..also I tried any and anyobject but not working

@maulikshah09
Copy link
Author

If i work with codable then it's working fine.

class EmptyModelWithData: Codable {
   var message = ""
   var status = ""
   var code = 0
   var data: ParseType?
}

enum ParseType: Codable {
   case stringValue(String)
   case arrayValue([String])
   init(from decoder: Decoder) throws {
       let container = try decoder.singleValueContainer()

       if let values = try? container.decode(String.self) {
           self = .stringValue(values)
           return
       }
       if let values = try? container.decode([String].self) {
           self = .arrayValue(values)
           return
       }

       throw DecodingError.typeMismatch(ParseType.self, DecodingError.Context(codingPath: decoder.codingPath, debugDescription: "Wrong type"))
   }

   func encode(to encoder: Encoder) throws {
       var container = encoder.singleValueContainer()
       switch self {
       case let .stringValue(x):
           try container.encode(x)
       case let .arrayValue(x):
           try container.encode(x)
       }
   }
}

@evermeer
Copy link
Owner

evermeer commented Jun 11, 2020

Ah, then I think a property converter would be the way to go.
Below is a code snipped that you could use.
You probably have to finetune it.

class EmptyModelWithData: EVObject {
   var message: String? 
   var status: String?
   var code: NSNumber?
   var data: ParseType?

    override func propertyConverters() -> [(key: String, decodeConverter: ((Any?) -> ()), encodeConverter: (() -> Any?))] {
        return [( key: "data", decodeConverter: {
           if let d = $0 as? String {
            self.data = ParseType(d)
           } else if let d = $0 as? NSArray {
             self.data - PareType(d)
           } else if let d = $0 as? NSDictionary {
             self.data - PareType(d)
           }
        },  encodeConverter: {
            return self.data
        })]
    }
}

here the propertyConverters function will return a decodeConverter for the data element. This means that when the data element should be parsed it will use that function. It will have a parameter of type Any which comes from the raw json serialization. So indeed it could be a NSArray, NSDictionary or a simple type like String. So the code just checks the type by casting it and then creating the right enum for it.

@maulikshah09
Copy link
Author

maulikshah09 commented Jun 11, 2020

Didn't get you... please provide me model.. so i can move ahead .. I think you can't understand my question.

One more time Explain you....

class EmptyModelWithData : EVObject {
    var message = ""
    var status  = ""
    var code = 0
    var data :  // array dictionay or string or  something else. Don't know the type 
}

I give you example for understanding..

1 ) if response is array

class EmptyModelWithData : EVObject {
var message = ""
var status = ""
var code = 0
var data : then auto add this array type
}

  1. if response is string then

class EmptyModelWithData : EVObject {
var message = ""
var status = ""
var code = 0
var data : ""
}

data variable type is not fixed.if you have proper solution then provide me..I waste my 5 days to solve this issue.Please provide me proper model of EVOBject.

@evermeer
Copy link
Owner

I updated my previous command so that it shows more of the model an dI added a comment

@maulikshah09
Copy link
Author

maulikshah09 commented Jun 11, 2020

Don't want codable ... I want using EVOBject...I have solved with codable

@maulikshah09
Copy link
Author

maulikshah09 commented Jun 11, 2020

If i work with codable then it's working fine.

class EmptyModelWithData: Codable {
   var message = ""
   var status = ""
   var code = 0
   var data: ParseType?
}

enum ParseType: Codable {
   case stringValue(String)
   case arrayValue([String])
   init(from decoder: Decoder) throws {
       let container = try decoder.singleValueContainer()

       if let values = try? container.decode(String.self) {
           self = .stringValue(values)
           return
       }
       if let values = try? container.decode([String].self) {
           self = .arrayValue(values)
           return
       }

       throw DecodingError.typeMismatch(ParseType.self, DecodingError.Context(codingPath: decoder.codingPath, debugDescription: "Wrong type"))
   }

   func encode(to encoder: Encoder) throws {
       var container = encoder.singleValueContainer()
       switch self {
       case let .stringValue(x):
           try container.encode(x)
       case let .arrayValue(x):
           try container.encode(x)
       }
   }

Check this answer ... I have already Solved using Codable .. I want this using EVObject..

@maulikshah09
Copy link
Author

maulikshah09 commented Jun 11, 2020

Please help me To go ahead I Stuck Here... My client is reviewing my code that's why I Finding a solution otherwise I go with codable.. Data is Generic Data type...

i want like this

class EmptyModelWithData : EVObject {
var message = ""
var status = ""
var code = 0
var data : Genric datatype --> it's store dictionary ,array or something else
}

@evermeer
Copy link
Owner

What do you mean? This should work:

class EmptyModelWithData: EVObject {
   var message: String? 
   var status: String?
   var code: NSNumber?
   var data: ParseType?

    override func propertyConverters() -> [(key: String, decodeConverter: ((Any?) -> ()), encodeConverter: (() -> Any?))] {
        return [( key: "data", decodeConverter: {
           if let d = $0 as? String {
            self.data = ParseType(d)
           } else if let d = $0 as? NSArray {
             self.data - PareType(d)
           } else if let d = $0 as? NSDictionary {
             self.data - PareType(d)
           }
        },  encodeConverter: {
            return self.data
        })]
    }
}

@maulikshah09
Copy link
Author

maulikshah09 commented Jun 12, 2020

Yes I have solved my issue using following code:

class EmptyModelWithData : EVObject {
    var message = ""
    var status  = ""
    var code = 00
    var data: Any?
    
    override func propertyConverters() -> [(key: String, decodeConverter: ((Any?) -> ()), encodeConverter: (() -> Any?))] {
        return [( key: "data", decodeConverter: {
            if let d = $0 as? String {
                self.data = d as AnyObject
            } else if let d = $0 as? NSArray {
                self.data = d
            } else if let d = $0 as? NSDictionary {
                self.data = d
            }
        },  encodeConverter: {
            return self.data
        })]
    }
}

@evermeer
Copy link
Owner

But then if you use any, you don't have to cast. This would be good enough:

class EmptyModelWithData : EVObject {
    var message = ""
    var status  = ""
    var code = 00
    var data: Any?
    
    override func propertyConverters() -> [(key: String, decodeConverter: ((Any?) -> ()), encodeConverter: (() -> Any?))] {
        return [( key: "data", decodeConverter: {
                self.data = $0
        },  encodeConverter: {
            return self.data
        })]
    }
}

I wonder if you need a property converter at all when you use Any? Or maybe you would not need it when its of NSObject?

@maulikshah09
Copy link
Author

maulikshah09 commented Jun 12, 2020

I have stuck Here. don't know what is the solution I have to converted In any But when I pass this model to main view controller then I want to convert on model... Is it possible..


data(Any) if array then convert

to

let arrPharmacy : [PharmacyInfo]?


data(Any) if dictionary then

to

let arrPharmacy : PharmacyInfo?


class PharmacyInfo{
    var Address1 = ""
    var Address2 = ""
    var City = ""
    var ContactPerson = ""
    var EmailId = ""
    var EntryDate = ""
    var IsDelete = ""
    var ModifyDate = ""
    var PId = ""
    var PharmacyName = ""
    var honeNumer = ""
    var serId = ""
}

@maulikshah09
Copy link
Author

Please reply on this...

@evermeer
Copy link
Owner

EVObject has an init with dictionary an Array as an init with dictionaryArray. So if you make the base class of PharmacyInfo EVObject then you could do this:
var myPI = PharmacyInfo(dictionary: theDictionary)
or
var myPIarray = [PharmacyInfo](dictionaryArray: theDictionaryArray)

@maulikshah09
Copy link
Author

Finally i got my solution...THanks...

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants