Objectmapper: Dynamic objects

Created on 20 Aug 2018  Β·  9Comments  Β·  Source: tristanhimmelman/ObjectMapper

Intro to issue

I looked around and I could not find any example of this structure, any suggestions on how to implement this with ObjectMapper are appreciated.

Your JSON dictionary:

{
"data" : [
    {
        "inspectionInfo": [                        
                 {
                   "fieldId" : "field1",
                   "value" : "field1",
                   "serverOperation": null
                },
                 {
                   "fieldId" : "field2",
                   "value" : "field2",
                   "serverOperation": null
                }
             ]
       },
       {
        "generalView": [
                 {
                   "fieldId" : "field1",
                   "value" : "field1",
                   "serverOperation": null
                },
                 {
                   "fieldId" : "field2",
                   "value" : "field2.",
                   "serverOperation": null
                }
             ]
       }  
  ]
}

Your model:

class InspectionResult: BaseResult {
    
    var inspection: Inspection?

    override func mapping(map: Map) {
        super.mapping(map: map)
        inspection <- map["data"]
    }
    
}


class Inspection: Mappable {
    var section: Array<Section>?
    required init?(map: Map) {
    }

    func mapping(map: Map) {
           //Assign the array
    }


}
class Section: Mappable {
    var fields: Array<Field>?

    required init?(map: Map) {
    }

    func mapping(map: Map) {
            //Assign the array
    }
}
class Field: Mappable {
    var fieldId: String?
    var serverOperation: String?
    var value: String?

    required init?(map: Map) {
    }

    func mapping(map: Map) {
        fieldId     <- map["fieldId"]
        serverOperation   <- map["serverOperation"]
        value   <- map["value"]

    }

}

Problem

"inspectionInfo" and "generalView" are sections, this json file can change anytime, add more sections or remove them. Each section contains a number of fields. Since I can not hardcode it and do inspectionInfo<- map["inspectionInfo"], how can I approach this?

All 9 comments

@TCNexmo your JSON in invalid. Please, update your issue and check if the data property is indeed a JSON array. If it is, the InspectionResult class will probably need to be something like this:

class InspectionResult: BaseResult {

    var inspection: [Inspection]?

    override func mapping(map: Map) {
        super.mapping(map: map)
        inspection <- map["data"]
    }
}

otherwise the value of inspection will be always nil.

@gcharita You were correct the JSON was invalid, I have corrected it. it was missing a couple of brackets in the example.

@TCNexmo sorry, but it still does not pass the validation check.

@gcharita corrected again, may I ask what do you use to validate it?

@TCNexmo please check again. Any online JSON validator or JSON formatter will do the job. (for example https://jsonformatter.org/)

Thank you for the tip, it should be correct now @gcharita

@TCNexmo the structure of your JSON is not optimal for the sections to be dynamic, however you can achieve this by creating a custom TransformType.

Assuming that every section in your data array has one key and that this key is the name of your section you can try the following custom TransformType:

class SectionsTransform: TransformType {
    typealias Object = [Section]
    typealias JSON = [[String: Any]]

    func transformFromJSON(_ value: Any?) -> Object? {
        guard let array = value as? [[String: Any]] else {
            return nil
        }
        return array.reduce(into: [Section](), { (result: inout [Section], dictionary: [String: Any]) in
            if let sectionName = dictionary.keys.first {
                let section = Mapper().map(JSON: dictionary, toObject: Section(sectionName: sectionName))
                result.append(section)
            }
        })
    }

    func transformToJSON(_ value: Object?) -> JSON? {
        return value?.toJSON()
    }
}

and you can modify your data structure like:

class InspectionResult: BaseResult {
    var sections: Array<Section>?

    override func mapping(map: Map) {
        super.mapping(map: map)
        sections <- (map["data"], SectionsTransform())
    }
}

class Section: Mappable {
    var sectionName: String!
    var fields: Array<Field>?

    init(sectionName: String) {
        self.sectionName = sectionName
    }

    required init?(map: Map) { }

    func mapping(map: Map) {
        fields <- map[sectionName]
    }
}

class Field: Mappable {
    var fieldId: String?
    var serverOperation: String?
    var value: String?

    required init?(map: Map) { }

    func mapping(map: Map) {
        fieldId <- map["fieldId"]
        serverOperation <- map["serverOperation"]
        value <- map["value"]
    }
}

Hope this helps.

I have not yet tried this solution, I will give some feedback once I have done it.

This implementation seems to work perfectly, thanks @gcharita

Was this page helpful?
0 / 5 - 0 ratings