Unresolved external when using TJSONValue::GetValue<T>()

Viewed 186

I'm using C++Builder XE6 and want to get the value part of some key/value pairs in JSON like this:

#include <System.JSON.hpp>

TJSONValue* Root = TJSONObject::ParseJSONValue("{\"MyString\":\"ABC\",\"MyBool\":true,\"MyObject\":{}}");

try
{
    String MyString = Root->GetValue<String>("MyString", String());
    bool MyBool = Root->GetValue<bool>("MyBool", false);
    TJSONObject* MyObject = Root->GetValue<TJSONObject*>("MyObject", NULL);
}
__finally
{
    delete Root;
}

The code compiles, but I get the following linker errors:

[ilink32 Error] Error: Unresolved external 'System::UnicodeString __fastcall System::Json::TJSONValue::GetValue<System::UnicodeString>(const System::UnicodeString, System::UnicodeString)' referenced from ...
[ilink32 Error] Error: Unresolved external 'bool __fastcall System::Json::TJSONValue::GetValue<bool>(const System::UnicodeString, bool)' referenced from ...
[ilink32 Error] Error: Unresolved external 'System::Json::TJSONObject * __fastcall System::Json::TJSONValue::GetValue<System::Json::TJSONObject *>(const System::UnicodeString, System::Json::TJSONObject *)' referenced from ...

Any idea what I'm doing wrong?

Update

For now, I decided to write some wrapper functions:

unit JSONUtils;

interface

uses
  System.JSON;

function GetJSONString (Value: TJSONValue; Path: string; Default: string = ''): string;
function GetJSONBool (Value: TJSONValue; Path: string; Default: Boolean = False): Boolean;
function GetJSONObject (Value: TJSONValue; Path: string; Default: TJSONObject = nil): TJSONObject;

implementation

function GetJSONString (Value: TJSONValue; Path: string; Default: string): string;
begin
  Result := Value.GetValue<string>(Path, Default);
end;

function GetJSONBool (Value: TJSONValue; Path: string; Default: Boolean): Boolean;
begin
  Result := Value.GetValue<Boolean>(Path, Default);
end;

function GetJSONObject (Value: TJSONValue; Path: string; Default: TJSONObject): TJSONObject;
begin
  Result := Value.GetValue<TJSONObject>(Path, Default);
end;

end.

I'm still getting the linker errors if I try to use the Delphi generics in C++, but at least the wrapper functions work.

1 Answers

This is covered in Embarcadero's documentation:

How to Handle Delphi Generics in C++:

Delphi generics are exposed to C++ as templates. However, it is important to realize that the instantiations occur on the Delphi side, not in C++. Therefore, you can only use these template for types that were explicitly instantiated in Delphi code.

...

C++ code can use the generics defined in Delphi directly as long as the C++ code limits itself to types for which the generic was instantiated on the Delphi side.

...

If C++ code attempts to use a Delphi generic for types that were not instantiated in Delphi, you'll get errors at link time.

...

To eliminate the error, you have to make sure that the Delphi code uses the [generic] type.

So, you would have to add a Delphi unit to your C++Builder project and have it access the Generic methods you want to use so that those symbols are then made available to the C++ linker.

If you can't get that working, then you won't be able to use the Generic version of the TJSONValue::GetValue() method. In your example, you can instead type-cast the Root pointer to TJSONObject* and then use the TJSONObject::Values[] property or TJSONObject::GetValue() method, eg:

#include <System.JSON.hpp>

TJSONValue* Root = TJSONObject::ParseJSONValue(_D("{\"MyString\":\"ABC\",\"MyBool\":true,\"MyObject\":{}}"));
try
{
    TJSONObject* Obj = static_cast<TJSONObject*>(Root);
    TJSONValue *Value;

    Value = Obj->GetValue(_D("MyString")); // or: Obj->Values[_D("MyString")]
    String MyString = (Value) ? Value->Value() : String();

    Value = Obj->GetValue(_D("MyBool")); // or: Obj->Values[_D("MyBool")]
    bool MyBool = (Value) ? static_cast<TJSONBool*>(Value)->AsBoolean : false;

    Value = Obj->GetValue(_D("MyObject")); // or: Obj->Values[_D("MyObject")]
    TJSONObject* MyObject = static_cast<TJSONObject*>(Value);
}
__finally
{
    delete Root;
}
Related