-2

I'm using SQLite with Swift to store some JSON in a table column. I convert a Codable object into a JSON String and store it in the "json" column in my "data" table.

id name json
1 item1 {"status" : true}
2 item2 {"status" : false}
struct Item : Codable {
    let status : Bool
}
//Decode with:
let decoder = JSONDecoder()
let item = try decoder.decode(Item.self, from: someStrigData)

The issue arises when I try to modify that stored JSON String using the SQLite JavaScript functions. SQLites uses 1 and 0 instead true/false and as a result, the JSON String fails to Decode.

To illustrate, the following SQL Query:

UPDATE data SET json = json_set(json, '$.status', false) WHERE name = "item1"

Will change the table the following way:

id name json
1 item1 {"status" : 0}
2 item2 {"status" : false}

I even tried to extract and use the Boolean value from hardcoded JSON String.

UPDATE data SET json = json_set(json, '$.status', json_extract('{"a": false}', '$.a') ) WHERE name = "item1"

But it did not help:

id name json
1 item1 {"status" : 0}
2 item2 {"status" : false}

I need a way to to force SQLite to use true/false instead of 0 and 1 OR I need a way for Swift to recognise 0 and 1 as true/false and correctly decode the JSON String into a Swift object.

I would like to note that the data here is for illustrative purposes and I actually use generics that conform to Codable, which means I can't implement custom encode/decode for each type because I want this to work for any object that conforms to Codable protocol.

Thanks.

mrtksn
  • 412
  • 3
  • 18
  • 1
    JSON, SQLite, Javascript, Swift, this is a very confusing question. Is it about decoding json or accessing SQLite (read, write or both?) and how is javascript relevant here? – Joakim Danielson Oct 19 '22 at 06:48

1 Answers1

0

You can customize init from decoder function like this way:

@propertyWrapper
struct Boolable: Decodable {
    let wrappedValue: Bool
    
    init(from decoder: Decoder) throws {
        let container = try decoder.singleValueContainer()
        
        if let stringValue = try? container.decode(String.self) {
            switch stringValue.lowercased() {
            case "false", "0":
                wrappedValue = false
            case "true", "1":
                wrappedValue = true
            default:
                wrappedValue = false
            }
        } else if let intValue = try? container.decode(Int.self) {
            switch intValue {
            case 0:
                wrappedValue = false
            case 1:
                wrappedValue = true
            default:
                wrappedValue = false
            }
        } else {
            wrappedValue = try container.decode(Bool.self)
        }
    }
}

struct Item: Decodable {
    @Boolable var status: Bool
}

And code mock for testing:

let mock = """
{
    "status": 1
}
""".data(using: .utf8)

let item = try! JSONDecoder().decode(Item.self, from: mock!)
print(item.status)
son
  • 1,058
  • 6
  • 7
  • That's a good solution but will still require modify the objects and i want to be able to deal with any Codable without requiring modify the structure. – mrtksn Oct 19 '22 at 09:00