bignerdranch / Freddy

A reusable framework for parsing JSON in Swift.
MIT License
1.09k stars 120 forks source link

Is there a public version of `valueAtPath` so that exceptions can be used, or else what is the alternative? #197

Closed Aquilosion closed 6 years ago

Aquilosion commented 8 years ago

While it's useful when I can use it, I often find that I cannot use JSONDecodable because either:

This poses a problem if I want to pass a partial JSON to an object. For example, if I have the following JSON:

{
    "test_object": {
        "a": 1,
        "b": 2,
        "c": 3
    }
}

And the following struct which takes in the contents:

struct TestObject {
    var a: Int
    var b: Int
    var c: Int

    var other: Int

    init(json: JSON, other: Int) throws {
        self.a = try json.int("a")
        self.b = try json.int("b")
        self.c = try json.int("c")

        self.other = other
    }
}

It appears to be impossible to initialise a TestObject with the contents of the test_object key without using some verbose code. Ideally, I would do something like this to create my instance, after loading the entire JSON above:

TestObject(json: try json.value("test_object"), other: 4)

This hypothetical value method would probably look something like the below in JSONSubscripting.swift, but I can find no such method:

public func value(path: JSONPathType...) throws -> JSON {
    return try valueAtPath(path)
}

Unfortunately I can't even extend JSON to provide this method myself, as valueAtPath is private. Instead, the closest things I can find to achieve the desired behaviour:

// Not ideal because the subscript returns an optional, and
// it would need to be manually unwrapped first:

TestObject(json: json["test_object"], other: 4)

// Not ideal because passing a dictionary to the other object
// just means the other object has to manually unwrap each
// value, and I feel the other object should be responsible for
// knowing that it's a dictionary rather than something else:

TestObject(json: try json.dictionary("test_object"), other: 4)

Is there something I'm missing, or is there something wrong with my general approach? Or, could Freddy benefit from a throwing value method?

zwaldowski commented 6 years ago

The design here was intentional. We intended for outside dependencies/modifications to be made through var and optional properties. I recognize this is not ideal, but it was a conscious choice made for complexity.