Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

JSONDecoder can't handle null response

Context

I am working with the Firebase Database REST API and JSONDecoder / JSONEncoder. It's been working pretty well so far. However for removing data the expected returned response is null, and JSONDecoder doesn't seem to like that very much.

This is the type of query I am sending via Postman and what I am getting back (sensitive data excluded).

DELETE /somedata/-LC03I3oHcLhQ/members/ZnWsJtrZ5UfFS6agajbL2hFlIfG2.json
content-type: application/json
cache-control: no-cache
postman-token: ab722e0e-98ed-aaaa-bbbb-123f64696123
user-agent: PostmanRuntime/7.2.0
accept: */*
host: someapp.firebaseio.com
accept-encoding: gzip, deflate
content-length: 39


HTTP/1.1 200
status: 200
server: nginx
date: Thu, 02 Aug 2018 21:53:27 GMT
content-type: application/json; charset=utf-8
content-length: 4
connection: keep-alive
access-control-allow-origin: *
cache-control: no-cache
strict-transport-security: max-age=31556926; includeSubDomains; preload

null

As you can see the response code is 200 and the body is null.

Error

When I receive the response this is the error I get :

Swift.DecodingError.dataCorrupted(Swift.DecodingError.Context(codingPath: [], debugDescription: "The given data was not valid JSON.", underlyingError: Optional(Error Domain=NSCocoaErrorDomain Code=3840 "JSON text did not start with array or object and option to allow fragments not set." UserInfo={NSDebugDescription=JSON text did not start with array or object and option to allow fragments not set.}))))

I tried creating a custom type (NoReply) to handle this as per a previous post but to no-avail.

Code

This is where the error happens :

        resource: {
            let decoder = JSONDecoder()
            decoder.dateDecodingStrategy = .iso8601
            return try decoder.decode(Resource.self, from: $0)
        },
        error: {
            let decoder = JSONDecoder()
            return try decoder.decode(FirebaseError.self, from: $0)
        }

So apparently even if I feed a custom NoReply type (as per the post mentioned above) JSONDecoder doesn't like null.

Any suggestions ?


As a side note this is what their documentation says about the response for a DELETE operation :

A successful DELETE request is indicated by a 200 OK HTTP status code with a response containing JSON null.

like image 791
Nick Avatar asked Jan 03 '23 01:01

Nick


2 Answers

Quick follow up

After a few successful hacks, the most elegant solution I came up with was the combination of :

  • using NoReply (as described by Zoul)
  • converting the null string to an empty JSON object structure ({})

So first, the conversion :

            if "null" == String(data: data, encoding: .utf8) {
                let json = Data("{}".utf8)

which is then fed back to the closure handling the request's response :

        resource: {
            let decoder = JSONDecoder()
            return try decoder.decode(Resource.self, from: $0)
        },

where the Resource is none other than :

public struct NoReply: Decodable {}

This works great now and allows me to handle the DELETE cases and GET cases on a non-existent object where it returns null.

Thanks for the help !

like image 152
Nick Avatar answered Jan 04 '23 14:01

Nick


Unfortunately JSONDecoder does not expose the underlying JSONSerialization option (.allowFragments) that would support JSON fragments like a solo null value. You could try transforming the response or just using JSONSerialization directly. Unfortunately there's nothing elegant to be done here.

like image 40
Jon Shier Avatar answered Jan 04 '23 15:01

Jon Shier