1

One colleague and I spend a few mins wondering why a class constructor was not called when Salesforce was reconstructing the class using JSON.deserialize(jsonString, apexType). I could not find any documentation that specifies that, but I expected the no-arg constructor to be called.

The deserialized class was something like this:

public class MyType {

  public String config;
  public final String defaultConfig;

  public MyType() {
    defaultConfig = 'anyValue';
  }

  public void calculate() {
    if (String.isEmpty(config)) config = defaultConfig;
    ...
  }

}

So, is this expected behaviour, or is this an extraneous versioned behaviour?

5

Yes, that's the intended behavior. Constructors are not called when you use JSON.deserialize. As an alternative approach, you can use getters:

public class MyType {

  public String config {
    public get { return String.isNotEmpty(config)? config: defaultConfig; }
    public set;
  }
  public String defaultConfig { get { return 'anyValue'; } }

}
2
  • Wow, you always bring a different perspective @sfdcfox, that is a good point. But have you found this behaviour documented? Mar 26 '21 at 9:43
  • 1
    @JefersonChaves I want to say that a long time ago, I found a document that clearly laid out this behavior, but when I looked for it recently, I wasn't able to find much beyond this post and a few similar questions like it. If such document exists, it's buried somewhere that the average person can't find it.
    – sfdcfox
    Mar 26 '21 at 12:10
4

While it isn't documented, it is the behaviour we've observed.

I think it is fair to say that the assumption is, in deserializing the object, all required state should already be in place based on that deserialized data. Sure, things like transients will be in their default state, but these likely need specific methods to be invoked to calculate them from the inputs and any state of the object anyway.

As an aside, personally I would avoid the sort of structure you have expressed in your example; for me, defaultConfig should be initialized in its declaration.

Edit:

In addition, according to Jeferson Chaves's findings, neither initializing member declarations nor instance initializer blocks are executed (search for "Instance initialization") when JSON.deserialize is used.

See @sfdcfox's excellent alternative solution to your initialization issue.

5
  • Good point Phill, I will try the instance initializer, the reason for defaultConfig was because I would have a child class that had a different default but calculate would be the same. Does it makes sense? Mar 25 '21 at 19:52
  • I would think that public final String defaultConfig = 'anyValue'; would mean this property is initialized (though perhaps not if the instance initializer doesn't work - I expect they are equivalent). Let me know what you find!
    – Phil W
    Mar 25 '21 at 20:41
  • 1
    What I have found is that neither the public final String defaultConfig = 'anyValue'; or instance initializer works. Mar 25 '21 at 21:55
  • 1
    At this point, I feel that then we should avoid mix content and behaviour when deserializing classes. This is counter-intuitive and not great, but I guess that is how this seems to be working. Mar 25 '21 at 21:57
  • There's an alternative, it's just not immediately obvious. Still a good answer, though.
    – sfdcfox
    Mar 25 '21 at 23:04

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.