Skip to content
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

Q: how to handle null in Json #41

Open
gsscoder opened this issue Aug 25, 2015 · 5 comments
Open

Q: how to handle null in Json #41

gsscoder opened this issue Aug 25, 2015 · 5 comments

Comments

@gsscoder
Copy link

When Json is something like

"{"query":{"count":1,"created":"2015-08-25T13:35:55Z","lang":"en-US","results":{"quote":{"symbol":"ORCL","AverageDailyVolume":"15153400","Change":"+0.00","DaysLow":null,"DaysHigh":null,"YearLow":"35.14","YearHigh":"46.71","MarketCapitalization":"156.45B","LastTradePriceOnly":"36.08","DaysRange":null,"Name":"Oracle Corporation Common Stock","Symbol":"ORCL","Volume":"2200","StockExchange":"NYQ"}}}}"

When I try to parse with Json.read DaysLow for example I get:

> System.Exception: couldn't use lens (<fun:totalLensPartialIsomorphism@98>, <fun:totalLensPartialIsomorphism@99-1>) on json 'Null null'
   at Microsoft.FSharp.Core.Operators.FailWith[T](String message)
   at Network.Yahoo.Finance.parseResponse(String json) in H:\YFinance.fs\src\YFinanceFs\Finance.fs:line 58
   at Network.Yahoo.Finance.getStockQuoteAsync@88-1.Invoke(String _arg1) in H:\YFinance.fs\src\YFinanceFs\Finance.fs:line 88
   at Microsoft.FSharp.Control.AsyncBuilderImpl.args@835-1.Invoke(a a)
--- End of stack trace from previous location where exception was thrown ---
   at Microsoft.FSharp.Control.AsyncBuilderImpl.commit[a](Result`1 res)
   at Microsoft.FSharp.Control.CancellationTokenOps.RunSynchronously[a](CancellationToken token, FSharpAsync`1 computation, FSharpOption`1 timeout)
   at Microsoft.FSharp.Control.FSharpAsync.RunSynchronously[T](FSharpAsync`1 computation, FSharpOption`1 timeout, FSharpOption`1 cancellationToken)
   at Network.Yahoo.Finance.getStockQuote(String symbol) in H:\YFinance.fs\src\YFinanceFs\Finance.fs:line 105
   at <StartupCode$FSI_0003>.$FSI_0003.main@()

Using Json.readOrDefault "DaysLow" String.Empty didn't solved the problem.

If json contains a null value I'd like to supply a default. Is this possible?

Thank you.

@gsscoder gsscoder mentioned this issue Aug 25, 2015
@kolektiv
Copy link
Member

Hmmm, interesting. I'll take a look at that this evening and work out what's happening there. :)

@gsscoder
Copy link
Author

👍

@kolektiv
Copy link
Member

Trying things out, I'm looking at something like this, which seems to work - but I'm not sure what code you're actually running! Feel free to post up more repro if this isn't helpful :)

    let txt = """{"query":{"results":{"low":null}}}"""
    let json = Json.parse txt

    let low =
            idLens
       <-?> Json.ObjectPIso
       >??> mapPLens "query"
       <??> Json.ObjectPIso
       >??> mapPLens "results"
       <??> Json.ObjectPIso
       >??> mapPLens "low"
       <??> Json.StringPIso

    let read =
            function | Some v -> v
                     | _ -> "default"
        <!> Json.tryGetLensPartial low

    let low =
        match read json with
        | Value low, _ -> low
        | _ -> failwith "unreachable"

@kolektiv
Copy link
Member

(On a side note, I think it's worth doing something which combines Json.ObjectPIso and mapPLens x, as that's a common combination. I'll look at that in the next release too, which will be shortly, to bring Chiron in to line with common naming convention for lenses)

@gsscoder
Copy link
Author

Thanks for comments and sample, @kolektiv. But the issue I've reported was before I've traversed json using lens.
The exception occur when Json.deserialize invoke StockQuote.FromJson. Some time some field from Yahoo service are null, what I'd like to do is being able to supply a default in such case.

This was the original parseResponse before partial lenses:

let private parseResponse json =
    match Json.parse json with
    | Object values ->
      match values |> Map.find "query" with
      | Object values ->
        match values |> Map.find "results" with
        | Object values -> 
          let node = values |> Map.find "quote"
          match node with
          | Array quotes ->
            quotes |> List.map (Json.deserialize : _ -> StockQuote)
          | Object _ ->
            [Json.deserialize node]
          | _ -> []
        | _ -> []
      | _ -> []
    | _ -> []

I don't know if I completely understood your reply... Do I have to define a Json traversal for each field that could be null? There's nothing more terse I can't do with Json.read in StockQuote.FromJson?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants