Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

StackOverflow in JSONObject #264

Closed
auricgoldfinger opened this issue Aug 10, 2016 · 11 comments
Closed

StackOverflow in JSONObject #264

auricgoldfinger opened this issue Aug 10, 2016 · 11 comments

Comments

@auricgoldfinger
Copy link

I noticed this when updating one of the libraries in our project. I used to serialise a dto which extends another one. This super class has a getter returning an Apache FastDateFormat, which is a singleton and returning a static instance of itself.

Previously this wasn't an issue because the JSONObject (JSONArray in my case) took a boolean specifying whether to includeSuperClass or not. By default, that was false and only the child class was serialised. The parameter is now gone and the default behaviour is that the super class is serialised as well. Hence my issue came up.

Given this class

public class StackOverflow {

   public static void main(String[] args) {
    //  final org.apache.commons.lang.time.FastDateFormat fdf = org.apache.commons.lang.time.FastDateFormat.getInstance();
      final MyDto dto = new MyDto("myvalue");
      new org.json.JSONObject(dto);
   }
}

Which uses these

public class MyDto {

   private String value1;
   private StaticMethodClass smc = StaticMethodClass.getInstance();

   public MyDto(String value1) { this.value1 = value1; }
   public String getValue1() {  return value1;  }
   public void setValue1(String value1) {   this.value1 = value1;  }
   public StaticMethodClass getSmc() { return smc; }
   public void setSmc(StaticMethodClass smc) {  this.smc = smc; }
}

public class StaticMethodClass {

   private static StaticMethodClass _instance;

   private StaticMethodClass() {}

   public static StaticMethodClass getInstance() {
      if (_instance == null) _instance = new StaticMethodClass();
      return _instance;
   }
}

Serializing the getInstance method will cause a stack overflow in JSONObject since it keeps being wrapped in JSONObjects (In theory, you can keep doing StaticMethodClass.getInstance().getInstance().getInstance().getInstance()...)

Of course, serialising an instance of StaticMethodClass would cause a Stackoverflow as well.

@johnjaylward
Copy link
Contributor

I don't recall ever seeing a constructor for JSONObject or JSONArray that took a boolean to ignore super classes. Do you recall what version you updated from? I took a look at the history of JSONObject but didn't see that constructor in any of the versions I looked at.

@johnjaylward
Copy link
Contributor

Also, that StaticMethodClass in your example is not thread safe (in case you need thread safety)

@johnjaylward
Copy link
Contributor

looking at the JSONObject::populateMap function It appears that the correct way to handle it would be to ignore static methods.

change this:

if (Modifier.isPublic(method.getModifiers())) {

to this:

if (Modifier.isPublic(method.getModifiers())
    && !Modifier.isStatic(method.getModifiers())) {

I don't see how exporting any static methods would be helpful for bean evaluation.

@stleary
Copy link
Owner

stleary commented Aug 11, 2016

Is static really the problem? What if the class had this method?

class MyClass {
      public getMyClass() { return new MyClass(); }
}

@johnjaylward
Copy link
Contributor

Either case would a problem, although I'm still not sure I see the benefit of including statics on a bean serialization. Statics usually aren't relevant to bean state.

I don't see any way to prevent this short of using a depth counter.

There are valid cases where we'd want to have something like your example work, like a tree structure.

@johnjaylward
Copy link
Contributor

If the user has development control over the classes in question, one could always just implement the JSONString interface to solve the issue.

The last option would be to force the use of the bean constructor that accepts the properties to export and list the ones you wanted.

@auricgoldfinger
Copy link
Author

In a previous version of the code, JSONArray used this JSONObject:

    public JSONObject(Object bean, boolean includeSuperClass) {
        this();
        populateInternalMap(bean, includeSuperClass);
    }

This is org.primefaces.json.JSONObject. PrimeFaces said they just incorporate org.json in their libraries and asked to file the bug report here so that they can update later on. Now I'm not sure whether they just include the code or whether they did do some modifications.

Any way, I quickly looked at the JSONString interface but I doubt that it will solve this particular issue. If MyDto implements JSONString then new JSONObject(myDto) will still produce the stackoverflow since the constructor will still try to serialise the object...

@johnjaylward
Copy link
Contributor

@auricgoldfinger that's good to note. I think that if a class implements JSONString, then the constructor should probably use that to generate the JSONObject.

I'll have to think on that more though, because JSONString can technically return any valid JSON Text, which could be an array, or even just a single value.

@stleary
Copy link
Owner

stleary commented Aug 15, 2016

@auricgoldfinger noted that you are also tracking this on the primefaces side. If you think this was previously supported in JSON-Java, please provide a reference, not sure that we can find one. One way or another, we need to work this to closure.

@auricgoldfinger
Copy link
Author

Sadly, they closed it on their side telling that I have to work a solution with you. While I think their version was once a modified one (you never had a parameter "includeSuperClass"), it's now up to you to decide what you're going to do. From my point of view, it would be a nice feature to have such a parameter anyway, so that I can make sure my child class is serialisable while its parent isn't. But you never had such a parameter so this would be a "feature request" more than a bug.

There is a work around for this issue: create a new dto which contains only getters and setters returning only primitive data types, fill it with the values you need and serialise that in stead of the original object. It's more work and requires more objects, but it's cleaner at the same time - from a JSON point of view.

Moreover, I don't think you can or even want to change the behaviour that a getter "getThis()" causes a StackOverflow. It's just plain wrong trying to serialise such an object because, well, you'll get a StackOverflow ;-)

@stleary
Copy link
Owner

stleary commented Aug 16, 2016

Glad to hear you have a workaround. It would be better to use it, unless someone comes up with a general case, where the useSuperclass param might be needed for other projects. Otherwise, agreed, some objects should not be serialized.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants