Duplicate entities in REST service call

I’m having a slight issue with accessing custom services over the REST API.

It seems that if a duplicate entity is detected within a particular call, only the ID pertaining to that entity is returned. I.e. the rest of the attributes are not returned. I can assume this behaviour is designed to reduce the amount of data that is returned.

I would like to turn this behaviour off, as it’s causing me issues with my use case (e.g. returning the same currency entity within a single row).

Is it possible to do this?

Many thanks!

Hi,
There is no any option that disables this compact format of the result, but you can override the encodeInstance method of the JSONConverter class and implement the desired behavior there.
Extend the JSONConverter bean:


public class MyJsonConverter extends JSONConverter {
    @Override
    protected MyJSONObject encodeInstance(Entity entity, Set<Entity> visited, MetaClass metaClass, View view) throws InvocationTargetException, NoSuchMethodException, IllegalAccessException {
        return super.encodeInstance(entity, visited, metaClass, view);
    }
}

and register the new bean in the portal-spring.xml (or web-spring.xml if you use rest api from the web-module):


<bean id="JSONConverter" class="com.company.resttest.portal.converter.MyJsonConverter"/>

BTW, we are working on new REST API now. It will return entities without replacement of duplicated references. We plan to release it in September, 2016.

Many thanks for the response Max.

Would you be able to provide a hint regarding the necessary changes in this method? I’ve been poking around in the code for a little while now - but really just via trial and error rather than based on any understanding of how the encoder works.

(P.S. great news re the standard encoder changes coming soon)

You can copy and paste the method implementation and remove the following code:


if (ref) {
      return root;
}

You also need to add the “primary” attribute to the new converter bean definition:


    <bean id="MyJSONConverter" class="com.company.resttest.portal.converter.MyJsonConverter" primary="true"/>

I’ve attached a demo project with a modified JsonConverter. Take a look at the MyJsonConverter class.

rest-test.zip (46.5K)

That’s perfect - thank you.

I do seem to be running into issues since switching to this method. It does work for some calls, but for others it returns the following exception:

2016-08-01 14:38:15.563 ERROR [http-nio-8080-exec-11/app-portal/admin] com.haulmont.cuba.restapi.DataServiceController - Error processing request: /app-portal/api/service.json?method=getPortfolioDetailsForManager&param0=cf7fcaa0-ab87-c644-805f-4572e0b5e97a&param0_type=java.lang.String&param1=a17e4fcd-cdba-796f-7fcb-9e7f876a1aa3&param1_type=java.lang.String&s=099de9c4-01ef-e546-9c13-1bb87169541a&service=clover_ValuationService
java.lang.StackOverflowError: null
at java.util.ResourceBundle.findBundle(ResourceBundle.java:1419) ~[na:1.8.0_72]
at java.util.ResourceBundle.findBundle(ResourceBundle.java:1419) ~[na:1.8.0_72]
at java.util.ResourceBundle.getBundleImpl(ResourceBundle.java:1361) ~[na:1.8.0_72]
at java.util.ResourceBundle.getBundle(ResourceBundle.java:1082) ~[na:1.8.0_72]
at org.eclipse.persistence.exceptions.i18n.ExceptionMessageGenerator.buildMessage(ExceptionMessageGenerator.java:62) ~[eclipselink-2.6.2.cuba6.jar:2.6.2.cuba6]
at org.eclipse.persistence.exceptions.ValidationException.instantiatingValueholderWithNullSession(ValidationException.java:1024) ~[eclipselink-2.6.2.cuba6.jar:2.6.2.cuba6]
at org.eclipse.persistence.internal.indirection.UnitOfWorkValueHolder.instantiate(UnitOfWorkValueHolder.java:233) ~[eclipselink-2.6.2.cuba6.jar:2.6.2.cuba6]
at org.eclipse.persistence.internal.indirection.DatabaseValueHolder.getValue(DatabaseValueHolder.java:101) ~[eclipselink-2.6.2.cuba6.jar:2.6.2.cuba6]
at org.eclipse.persistence.indirection.IndirectSet.buildDelegate(IndirectSet.java:218) ~[eclipselink-2.6.2.cuba6.jar:2.6.2.cuba6]
at org.eclipse.persistence.indirection.IndirectSet.getDelegate(IndirectSet.java:388) ~[eclipselink-2.6.2.cuba6.jar:2.6.2.cuba6]
at org.eclipse.persistence.indirection.IndirectSet.size(IndirectSet.java:569) ~[eclipselink-2.6.2.cuba6.jar:2.6.2.cuba6]
at com.haulmont.cuba.core.global.GlobalPersistentAttributesLoadChecker.checkIsLoadedWithGetter(GlobalPersistentAttributesLoadChecker.java:99) ~[cuba-global-6.2.3.jar:6.2.3]
at com.haulmont.cuba.core.global.GlobalPersistentAttributesLoadChecker.isLoadedSpecificCheck(GlobalPersistentAttributesLoadChecker.java:91) ~[cuba-global-6.2.3.jar:6.2.3]
at com.haulmont.cuba.core.global.GlobalPersistentAttributesLoadChecker.isLoaded(GlobalPersistentAttributesLoadChecker.java:65) ~[cuba-global-6.2.3.jar:6.2.3]
at com.haulmont.cuba.core.global.PersistenceHelper.isLoaded(PersistenceHelper.java:118) ~[cuba-global-6.2.3.jar:6.2.3]
at com.company.portal.NonCompactJSONConverter.encodeInstance(NonCompactJSONConverter.java:76) ~[app-portal-0.1-SNAPSHOT.jar:na]
at com.company.portal.NonCompactJSONConverter.encodeInstance(NonCompactJSONConverter.java:126) ~[app-portal-0.1-SNAPSHOT.jar:na]
at com.company.portal.NonCompactJSONConverter.encodeInstance(NonCompactJSONConverter.java:126) ~[app-portal-0.1-SNAPSHOT.jar:na]
at com.company.portal.NonCompactJSONConverter.encodeInstance(NonCompactJSONConverter.java:126) ~[app-portal-0.1-SNAPSHOT.jar:na]
at com.company.portal.NonCompactJSONConverter.encodeInstance(NonCompactJSONConverter.java:143) ~[app-portal-0.1-SNAPSHOT.jar:na]
at com.company.portal.NonCompactJSONConverter.encodeInstance(NonCompactJSONConverter.java:126) ~[app-portal-0.1-SNAPSHOT.jar:na]
… SNIP …

Another reason the compact format is used here is to avoid cyclic dependencies. It seems that you have such dependency in the returned entity.
For now, I see only one quick fix. The implementation is in the attached file.
What I did there is that I changed nested invocations of encodeInstance method from this:


encodeInstance((Entity) value, visited,
                                    property.getRange().asClass(), propertyView)

to this:


encodeInstance((Entity) value,  new HashSet<>(visited),
                                    property.getRange().asClass(), propertyView)

After these changes, the repeated entities will only be replaced if they are under the same graph branch.
E.g. if EntityA has two fields of type EntityB, then none of EntityB fields will be compact. But if entityB has a reference to the entityA, then this reference will be in the compact format.

rest-test.zip (49.7K)

Thanks again Max. Implementing this locally all looks to be working fine.

Appreciate the help here!