nixzhu / Baby

Create models from a JSON file, even a Baby can do it.
https://apps.apple.com/cn/app/ducky-model-editor/id1525505933
MIT License
222 stars 21 forks source link

Hmmm. What should these do... #18

Open dnedrow opened 6 years ago

dnedrow commented 6 years ago

Given the following JSON...

{
  "code" : "SUCCESS",
  "positions" : [ {
    "asOf" : "2018-03-30T04:00:00.000Z",
    "description" : "***Euro ",
    "shortName" : "CASH - EURO",
    "tradeActions" : [ ]
  } ]
}

Codable conversion

The default Codable conversion for this using command line baby is...

struct MyModel: Codable {
    let code: String
    struct Position: Codable {
        let asOf: Date
        let description: String
        let shortName: String
        let tradeActions: [Any] //TODO: Specify the type to conforms Codable protocol
    }
    let positions: [Position]
}

array-object-map and enum-properties issues

My question revolves around the TODO. In this case, the values of the tradeActions array are items from an enumerated set, TradeAction. The TradeAction enum has members like "SELL" and "BUY".

The baby help seems to indicate that I can specify the type of the tradeActions array, and also declare the TradeAction enum.

So here's the baby command line I used...

baby -i t01short.json --codable --model-type struct --model-name DWMPositionsModel --json-dictionary-name "[String: Any]" --convert-from-snake-case --array-object-map "tradeActions: [TradeAction]" --enum-properties "type, TradeAction[BUY_MORE, BUY_TO_CLOSE]"

My expectation is that I would receive something similar to the following...

struct MyModel: Codable {
    let code: String
    struct Position: Codable {
        let asOf: Date
        let description: String
        let shortName: String
        let tradeActions: [TradeAction]
    }
    let positions: [Position]
    enum TradeAction {
        case BUY_MORE
        case BUY_TO_CLOSE
    }
}

However, the output is identical to the initial Codable conversion.

The array-object-map and enum-properties don't appear to actually do anything. Or am I not using them right? I see the same behavior when using CuteBaby.

nixzhu commented 6 years ago

@dnedrow Hi, I think the issue is that tradeActions is an empty array, baby can not infer it's type. And Array Object Map is only work for object array (not number or string array).

If you change the json to

{
  "code": "SUCCESS",
  "positions": [
    {
      "asOf": "2018-03-30T04:00:00.000Z",
      "description": "***Euro ",
      "shortName": "CASH - EURO",
      "tradeActions": [
        "BUY_MORE",
        "BUY_TO_CLOSE"
      ]
    }
  ]
}

with Enum Properties: tradeActions, you can get

struct DWMPositionsModel: Codable {
    let code: String
    struct Position: Codable {
        let asOf: Date
        let description: String
        let shortName: String
        enum TradeActions: String, Codable {
            case bUYMORE = "BUY_MORE"
            case bUYTOCLOSE = "BUY_TO_CLOSE"
        }
        let tradeActions: [TradeActions]
    }
    let positions: [Position]
}

It's not perfect but you can modify it later.

nixzhu commented 6 years ago

@dnedrow Maybe it's better to do it as a computed property (with Property Map: tradeActions: _tradeActions and Enum Properties:tradeActions[sell:SELL,buy:BUY]), you will get:

struct DWMPositionsModel: Codable {
    let code: String
    struct Position: Codable {
        let asOf: Date
        let description: String
        let shortName: String
        enum TradeActions: String, Codable {
            case sell = "SELL"
            case buy = "BUY"
        }
        let _tradeActions: [TradeActions]
        private enum CodingKeys: String, CodingKey {
            case asOf
            case description
            case shortName
            case _tradeActions = "tradeActions"
        }
    }
    let positions: [Position]
}

Now, you can modify it as follow:

struct DWMPositionsModel: Codable {
    let code: String
    struct Position: Codable {
        let asOf: Date
        let description: String
        let shortName: String
        enum TradeAction: String, Codable { // modify enum name
            case sell = "SELL"
            case buy = "BUY"
        }
        let _tradeActions: [String] // modify type
        var tradeActions: [TradeAction] { // add a new computed property
            return _tradeActions.compactMap({ TradeAction(rawValue: $0) })
        }
        private enum CodingKeys: String, CodingKey {
            case asOf
            case description
            case shortName
            case _tradeActions = "tradeActions"
        }
    }
    let positions: [Position]
}

If the TradeAction has some new cases in the future, your old model will not break.

dnedrow commented 6 years ago

That's an interesting solution. Thanks for the clarification.

Can you enable the Wiki for baby? I'd like to post examples for others that may have similar problems.

dnedrow commented 6 years ago

Or maybe it would be better if I created a markdown file of examples and submitted it as a PR here. That way it would be available when the developer downloads the code.

nixzhu commented 6 years ago

@dnedrow Wiki enabled, welcome to post your examples. I'm sorry Baby has not much document.

nixzhu commented 6 years ago

@dnedrow PR is also welcome.