5

I'm using Delphi XE3. I have a JSON stream where an object can be null. That is, I can receive:

"user":null

or

"user":{"userName":"Pep","email":"[email protected]"}

I want to discriminate both cases, and I tried with this code:

var 
  jUserObject: TJSONObject;

jUserObject := TJSONObject(Get('user').JsonValue);
if (jUserObject.Null) 
then begin
  FUser := nil;
end else begin
  FUser := TUser.Create;
  with FUser, jUserObject do begin
    FEmail := TJSONString(Get('email').JsonValue).Value;
    FUserName := TJSONString(Get('userName').JsonValue).Value;
  end;
end;

If I put a breakpoint right in line if (jUserObject.Null) then begin and I mouse over jUserObject.Null it says jUserObject.Null = True if "user":null and it says jUserObject.Null = False if "user":{"userName":"Pep","email":"[email protected]"}

However, if I step into that line with the debugger, jUserObject.Null calls the following XE3 library code:

function TJSONAncestor.IsNull: Boolean;
begin
  Result := False;
end;

So I always get a False for my if sentence, even if "user":null.

I suppose I always have the workaround of catching the exception that is raised when "user":null and Get('email').JsonValue is executed in order to discriminate if the value is null or not, but that does not seem so elegant.

How is one supposed to detect if an JSON object has a null value in the JSON stream?

2 Answers 2

7

Get() returns a TJSONPair. When you have "user":null, the TJSONPair.JsonValue property will return a TJSONNull object, not a TJSONObject object. Your code is not accounting for that possibility. It assumes the JsonValue is always a TJSONObject and not validating the type-cast.

There are two ways to handle this.

  1. TJSONPair has its own Null property that specifies whether its JsonValue is a null value or not:

    var
      JUser: TJSONPair;
      jUserObject: TJSONObject;
    
    jUser := Get('user');
    if jUser.Null then begin
      FUser := nil;
    end else begin
      // use the 'as' operator for type validation in
      // case the value is something other than an object...
      jUserObject := jUser.JsonValue as TJSONObject;
      ...
    end;
    
  2. Use the is operator to test the class type of the JsonValue before casting it:

    var
      JUser: TJSONPair;
      jUserObject: TJSONObject;
    
    jUser := Get('user');
    if jUser.JsonValue is TJSONNull then begin
      FUser := nil;
    end
    else if jUser.JsonValue is TJSONObject then begin
      jUserObject := TJSONObject(jUser.JsonValue);
      ...
    end else begin
      // the value is something other than an object...
    end;
    
Sign up to request clarification or add additional context in comments.

5 Comments

That a value is not a JSON object does not imply that it is null. For instance it could be a JSON array.
@DavidHeffernan: Hence my suggestions to use the as and is operators to validate the value really is a TJSONObject object before accessing it as a TJSONObject object. Given the OP's requirement, an array would be an erroneous type.
If the data is user supplied then erroneous data is quite possible. So I'd prefer to detect that rather than assume non-object is null. But your edit deals with that. Thanks.
@RemyLebeau how would a user writing c++ use as and is operators?
@HappyCoding use C++'s dynamic_cast for objects, and the RTL's Sysutils::Supports() function for interfaces.
3

You've made the common mistake of confusing JSON objects with Delphi objects. The TJSONObject class represents JSON objects only, which are never null because null is distinct from {...}. TJSONObject is not the ancestor for all JSON values, like your code assumes. TJSONValue is.

Don't type-cast your "user" value to TJSONObject until you know it's an object. Check the Null property first, then type-cast.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.