The Wiert Corner – irregular stream of stuff

Jeroen W. Pluimers on .NET, C#, Delphi, databases, and personal interests

  • My badges

  • Twitter Updates

  • My Flickr Stream

  • Pages

  • All categories

  • Enter your email address to subscribe to this blog and receive notifications of new posts by email.

    Join 2,419 other followers

How to debug Delphi JSON export stack-overflows: watch the fields and their circular references

Posted by jpluimers on 2020/12/23

Unlike Delphi RTL XML support which is property based, the JSON support is field based.

By default, JSON uses all fields (no matter their protection level, so anything from strict private to published  is taken into account).

When there are cycles, they are not detected: it will just stack-overflow with a high set of entries like this:

REST.JsonReflect.{REST.JsonReflect}TTypeMarshaller.MarshalValue((($A9B7E8, Pointer($AFE168) as IValueData, 80, 336, 2024472912, $78AB0150, TClass($78AB0150), 80, 336, 2024472912, 2,77471682335019e+34, 1,00022251675539e-314, 0,00000000737961e-4933, 2024472912, 202447,2912, 2024472912, 2024472912, ($78AB0150, nil), $78AB0150)),???)

The easiest way to debug this is to:

  1. Set breakpoints in procedure TTypeMarshaller<TSerial>.MarshalData(Data: TObject);
    1. First breakpoint on the for rttiField loop
      • Watch or log these values (the first two usually are the same, the last two too):
        1. ComposeTypeName(Data) which gives you the fully qualified name (including unit and class) of the type exposing the fields
        2. Data.ClassName as a sanity check
        3. rttiType.Name which should be the same as Data.ClassName
    2. Second breakpoint inside the for rttiField loop on the if not ShouldMarshalstatement
      • Watch or log these values:
        1. rttiType.Name inside the loop, it changes value to match rttiField.Name, because of a debugger bug not showing it as E2171 Variable 'rttiType' inaccessible here due to optimization.
        2. rttiField.Name the actual field name

Tricks to circumvent circular references:

  • remember that fields with a reference to function value are not marshaled, so they are an excellent way of shoehorning in a dependency in (the reference value will be a capture which includes the instance data of the function to be called)
  • applying a [JsonMarshalled(False)] attribute (be sure to use unit REST.Json.Types!) only works when used inside non-generic types:
    • a class like TMySoapHeaderValue<T: IInterface> = class will not expose these attributes
    • a class like TMySoapHeaderValue = class will expose these attributes

You can check the JsonMarshalled problem by setting a breakpoint inside function LazyLoadAttributes(var P: PByte): TFunc<TArray<TCustomAttribute>>; on the line Exit(nil); and watch the value for Handle^.

Leave a Reply

Fill in your details below or click an icon to log in: Logo

You are commenting using your account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

This site uses Akismet to reduce spam. Learn how your comment data is processed.

%d bloggers like this: