json-schema-to-elm
json-schema-to-elm copied to clipboard
Validate pattern against regex when encoding data into JSON
Entries of type string can get a pattern, restricting the value space.
Arguably, the generated Elm code should prevent invalid JSON from being created, so it'd have to verify that the patterns match.
By 'should prevent invalid JSON from being created' do you mean when an Elm type, Foo, is encoded back into a JSON string, encodeFoo?
Note that json-schema-to-elm does not validate JSON schema files, that is the job of other tools.
Yes, that is what I meant.
Note that json-schema-to-elm does not validate JSON schema files, that is the job of other tools.
I understand that, but not how this fact relates to my proposal? (Which is not about validating schemas or validating inputs against schemas, but generating only valid (JSON) strings out of Elm.)
Meant no offence, just had to make sure there weren't any misunderstandings.
If possible, I'd appreciate any example JSON schemas and/or expected Elm code generated.
Depending on the use case, it might also make sense to
- autogenerate Elm html input forms based on the JSON schema, and
- let the html input make the check against the
patterninstead of the encoding function.
None taken, I was honestly confused. I guess I could have communicated that better, my bad!
Consider for instance this simple schema:
{
"$schema": "http://json-schema.org/draft-06/schema#",
"id": "http://some.url",
"type": "object",
"properties": {
"big-numbers": {
"type": "string",
"pattern": "^[0-9]+(\\.[0-9]+)?$"
}
}
}
The generated encoder is:
encodeNumbers : Numbers -> Value
encodeNumbers numbers =
let
bigNumber =
case numbers.bigNumber of
Just bigNumber ->
[ ( "bigNumber", Encode.string bigNumber ) ]
Nothing ->
[]
in
object <|
bigNumber
We see that the pattern is not enforced; { big-numbers : Just "abc" } would just be encoded as JSON.
I would imagine something like this (please excuse my shitty Elm) could work:
bigNumbersPattern : Regex
bigNumbersPattern = Regex.regex "^[0-9]+(\\.[0-9]+)?$"
encodeNumbers : Numbers -> Value
encodeNumbers numbers =
let
bigNumber =
case numbers.bigNumber of
Just bigNumber ->
let
bigNumberString = Encode.string bigNumber
in
case bigNumberPattern.contains bigNumberString of
True ->
[ ( "bigNumber", Encode.string bigNumber ) ]
False ->
[] -- Report error here?
Nothing ->
[]
in
object <|
bigNumber
Side notes, just some things I noticed: When I forgot id, the error message was nice but pointed at #. When I then forgot the top-level type, the output was silently nothing. Forgetting the top-level title silently produces invalid Elm (the identifiers are empty). Having dashes in titles/names in JSON schemas (valid) silently produces invalid Elm.
Thanks for the example code :)
Hmm, so by validating against the regex in the encoder it would necessarily end up introducing the concept of error handling to the generated Elm, i.e. so some encoders would end up returning a Maybe Value or Result String Value instead of just a Value, which again would have to be accounted for any encoder that uses that encoder.
Given that a decoder can also produce an error, this might not be so bad, but it does require a change across encoders. I'll think about it.
Regarding the bugs, I'm currently aware that there is a selections of various minor bugs that needs to be fixed and I'm planning to do a bug fixing round within the next week or two, depending on my other work loads, that should make json-schema-to-elm feel more smooth.
Given that a decoder can also produce an error, this might not be so bad, but it does require a change across encoders. I'll think about it.
True. I don't know how much you've implemented, but JSON Schema has quite a lot of involved restrictions ("If value A is set, B should follow this schema; other wise this other one") which I don't think can be nicely handled by the type system. So if you plan to expand the coverage of JSON Schema features, error handling will probably be unavoidable, I think. If generating only conforming JSON is a requirement you want to cover, that is.
Regarding the bugs, I'm currently aware that there is a selections of various minor bugs that needs to be fixed and I'm planning to do a bug fixing round within the next week or two, depending on my other work loads, that should make json-schema-to-elm feel more smooth.
No worries. I just wanted to mention them since I know very well how you can be blind to the most obvious thing when coding your own thing. X-)
Some quick brainstorming for doing a Result-passing version of encoders, maybe something like this (can most likely be simplified further):
-- Types
type alias User =
{ username : String
, favoriteNumber : Maybe String
}
type alias Error =
String
type alias Property =
( String, Value )
-- Encoders
encodeObject : Result (List Error) (List Property)
encodeObject =
Ok []
encodeUser : User -> Result (List Error) Value
encodeUser user =
encodeObject
|> encodeUsername user.username
|> encodeFavoriteNumber user.favoriteNumber
|> Result.map object -- would be nice to merge this line into the encodeObject function
encodeUsername :
String
-> Result (List Error) (List Property)
-> Result (List Error) (List Property)
encodeUsername username =
Result.map <|
(++) [ ( "username", Encode.string username ) ]
encodeFavoriteNumber :
Maybe String
-> Result (List Error) (List Property)
-> Result (List Error) (List Property)
encodeFavoriteNumber favoriteNumber =
case favoriteNumber of
Just number ->
if Regex.contains (Regex.regex "^[0-9]+(\\.[0-9]+)?$") number == True then
Result.map <|
(++) [ ( "bigNumber", Encode.string number ) ]
else
Result.mapError <|
(++) [ "<appropriate error message>" ]
Nothing ->
Result.mapError <|
(++) [ "<appropriate error message>" ]