-
Notifications
You must be signed in to change notification settings - Fork 0
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add parse type [Any] with JsonValue #9
base: master
Are you sure you want to change the base?
Conversation
Traverses an [Any]'s elements. Finds whether they all conform to JsonValue types.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@Honga1, thank you for your time contributing to Stork!
I left a review explaining extensively what I don't see as a good addition to the library and, on other parts, what I don't find useful enough in the proposed changes and how we could make it more useful.
Please have a look and I hope to have a further discussion on this 👍
@@ -25,6 +25,12 @@ public enum JsonValue { | |||
self = .string(value) | |||
case let value as JSON: | |||
self = .object(value) | |||
case let value as [JsonValue]: | |||
self = .array(value) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't think this is useful. We want to build a JsonValue from some primitive/basic type. If you have already have a [JsonValue]
, JsonValue.init was already called directly by the user, so instead of having to do:
let asArray: [JsonValue] = [.string("hello"), .string("world")]
let asJsonValue: JsonValue? = JsonValue.init(asArray)
It's simpler to just do:
let jsonValue = .array([.string("hello"), .string("world")])
case let value as [Any]: | ||
self = .array(value.map { | ||
JsonValue.init(fromAny: $0)! | ||
}) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This one is useful in theory, although I am not sure if ever useful in practice.
To parse an array of type T from a JSON object, we'd use json ..? "field"
or json ..! "field"
. In such a case, the T is inferred and Stork knows how to build values of that type T as long as it complies to FromJson
.
In this proposed case, we would start with a value whose type is unknown, and we want to check whether it is an array of Any type. If so, we'd return array([JsonValue])
, which - in any normal case - is still not good enough for a user. Having array([JsonValue])
, we'd still need to parse it to a final, useful type:
case let .array(xs):
let ys: [MyType] = xs.compactMap { decodeValue($0) }
return .array(ys)
But that could be achieved in a single step by doing:
// At a high-level
let ys = [MyType].from(jsonArray: jsonArray) // where jsonArray is [JSON]
// Or when parsing a field from an object
array: json ..! "field"
return nil | ||
} | ||
} | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The same goes about this one. This would only be useful to define that a type T: FromJson
can be built from a JSON array.
For example, consider a simple ReadingList
type:
struct ReadingList {
let books: [Book]
}
This ifArray
method would be useful like so:
public static func from(value: JsonValue) -> ReadingList? {
return value.ifArray { array in
ReadingList(books: array.compactMap { Book.from(value: $0) })
}
}
Now, note that this represents much more work than the following alternatives:
// At a high-level
let readingList = ReadingList(
books: [Book].from(jsonArray: booksArray)
)
// Or when parsing a field from an object
readingList: ReadingList(books: try json ..! "books")
Alternatives
Despite these arguments, I do like the idea to support arrays as Stork does for all other json value types.
Alternative 1
A way of having them in a useful, more convenient way would be to add a convenient way of building an [T]
from [JsonValue]
so that the ifArray
scenario becomes:
public static func from(value: JsonValue) -> ReadingList? {
return value.ifArray { array in
ReadingList(books: [Book].from(jsonArray: array))
}
}
Or a way of inferring the type desired to be obtained from [JsonValue]
, so to have:
public static func from(value: JsonValue) -> ReadingList? {
return value.ifArray { array in
ReadingList(books: array.parsed())
}
}
Or even better:
public static func from(value: JsonValue) -> ReadingList? {
return ReadingList(books: value.parsedArray())
}
Alternative 2
Introduce a FromJsonArray<T>
protocol, so that the final type can be immediately inferred and having Stork only requiring types that are built from JSON arrays to do:
extension ReadingList: FromJsonArray<Book>
Although it looks nice, it'd introduce incongruity to the simple Stork design of FromJson
, as now the parsers would need to handle the FromJson
and FromJsonArray<T>
cases.
Conclusion
I am open for discussion. I like the idea of supporting fully arrays for a matter of consistency but I don't see the objective benefit of doing so.
If we introduce this feature, I'd prefer to neat pick the simpler solutions proposed in the Alternative 1
.
Hi @NunoAlexandre. I'm too far away from this now to be able to go over this in detail. Happy if I close this one off due to inactivity? |
Currently I can not see where JsonValue implements the array type. I see that it has a case for it, without supporting it in the init or the If functions.
This merge adds these two components. It allows traversal of input [Any]'s elements, and checks/converts the sub types to the appropriate format if possible.