tristanhimmelman / ObjectMapper

Simple JSON Object mapping written in Swift
MIT License
9.16k stars 1.03k forks source link

Question: Best way to handle different deserialization across modules #1091

Open theladyjaye opened 4 years ago

theladyjaye commented 4 years ago

Your model:

// MODULE: Models
public struct MyModel {
  var name: String!
    public init(){}
}

What you did:

// MODULE: Api
import Models
extension MyModel: StaticMappable{
    public static func objectForMapping(map: Map) -> BaseMappable?{
         return MyModel()
     }
     mutating public func mapping(map: Map) {
        name                 <- map["nameFromAPIRequestField"]
    }
}
// MODULE: LocalCache
import Models
extension MyModel: StaticMappable{
    public static func objectForMapping(map: Map) -> BaseMappable?{
         return MyModel()
     }
     mutating public func mapping(map: Map) {
        name                 <- map["name"]
    }
}

What you expected:

So, I have extensions in different modules that require different kinds of deserialization. In the above example, the response from the API has the field nameFromAPIRequestField which is beyond my control; no worries, it maps easily enough. Now enter the issue. We don't want to make API request all the time for everything, so we Cache these, but we also restructure things for the local cache storage engine. In this case when we write the cache record (JSON Document Store (Couchbase in this case)) and use the field name name instead of the API response field nameFromAPIRequestField.

At runtime we will get:

_TtGC12ObjectMapper6MapperV15Models7MyModel is implemented in both ?? (0x1064347e0) and ?? (0x106437920). One of the two will be used. Which one is undefined.

That makes sense as we have 2 modules loaded and BOTH have the same extension methods declared. Stepping through the code it looks like it's just confused on which objectForMapping to use and it uses the correct mapping(_: Map) function.

Now I THINK a way around this would be just just make module specific wrappers to remove the ambiguity for objectForMapping and just have it return the object and have an empty mapping(_: Map) for that wrapper. Next be be sure to:

// MODULE: LocalCache

extension MyModel: BaseMappable{
   mutating func mapping(map: ObjectMapper.Map){...}
}

Is there an alternative way or does this methodology seem sound?

mojidabckuu commented 4 years ago

I bet it would be achievable by using different contexts if your modules are just a convenient way to structure the project imho Another way to solve your problem is to use difference transformers/mappers/serializers explicitly as you mentioned. Remember that if your data class has private props then you have to assign them manually.