Xstream serialization of a ReportData instance

Unproxying the proxy before marshalling.

All i need is just a simple test to copy, paste, and run to reproduce the problem. :smile:

You mean that I should replace proxy instances with their actual implementations within the ReportData instance before invoking the marshalling on it? Do you know of a utility that could scan an object entirely to find (and replace) every nested proxy instances?

Through this thread you will see that I have used two methods for marshalling:

  1. With ReportingSerializer as suggested by Mike.
  2. With Xstream direclty, and that is what most of the thread is about. I provided you with a resource for 2, not yet for 1.

1) Unmarshalling with ReportingSerializer (do you want me to generate a resource for this?)

ReportingSerializer serializer = new ReportingSerializer();
ReportData reportData1 = serializer.deserialize(str, ReportData.class);

2) Unmarshalling with Xstream (you have a sample XML for this):

XStream xstream = new XStream();
reportData = (ReportData) xstream.fromXML(str);

@dkayiwa, this is the same instance of ReportData

1) marshalled with ReportingSerializer: http://paste.ubuntu.com/15179203/

2) marshalled directly with Xstream: http://paste.ubuntu.com/15177033/

Yes, I will try this:

I don’t know of any utility though. If it’s just the DUser object you can do custom code. Doing it by reflection and recursive is dangerous because you may find circular references and go into an endless loop.

In my understanding, this is what com.thoughtworks.xstream.hibernate.converter.HibernateProxyConverter is meant to do. Look in particular at the marshall(..) method implementation:

final Object item = ((HibernateProxy)object).getHibernateLazyInitializer().getImplementation();

That is exactly what is suggested in the stackoverflow answer. I have mentioned this in an earlier response to your initial suggestion, and although this produces a slightly different XM, the main issue doesn’t go away.

To be honest my feeling is that Mike’s idea is the right one, ie using ReportingSerializer. Unfortunately, as you could read earlier in this thread, I then stumbled upon yet another issue when using it. I wish he could bounce back on my answer to him.

I agree that you should use ReportingSerializer. I thought you wanted to use XStream too.

Maybe XStream only deproxies the object to be marshalled (ReportData) but not its attributes that can be Hibernate proxies too, as it is the case with audit info.

Oh no, I started with Xstream simply because I didn’t know of ReportingSerializer. I am totally fine to use it.

Would you have any idea as to why this issue is happening with LinkedHashMap when unmarshalling with ReportingSerializer?

@dkayiwa, you should be stumbling upon the same issue with the resource I provided.

@mksd the paste links do not have the exact unit test to reproduce. So i left it for those who may have time to rebuild the exact unit test from the pieces in this thread. :smile:

You could add the below class in any module’s api src/test/java:

public class MyTest extends BaseModuleContextSensitiveTest {
  @Test
  public void foo() throws IOException, SerializationException {
    InputStream inStream = getClass().getClassLoader().getResourceAsStream("sampleReportData.xml");
    String str = IOUtils.toString(inStream1, "UTF-8");

    ReportingSerializer serializer = new ReportingSerializer();
    ReportData reportData = serializer.deserialize(str, ReportData.class);
}

Then paste the XML in a test resource named sampleReportData.xml within src/test/resources.

I don’t quite have a GitHub repo, I am myself pasting this in & out as I’m trying further steps, but it is quite trivial to setup the above.

Your best bet is to debug and see why it happens. Could be a bug in the serializer.

@mksd am actually interested in the serialisation process which produces invalid values. So if you can share the test you used to create the sample xml with invalid user values, that would be a great place for me to start investigating!

@mksd and @lluismf , to be clear the entire purpose of the ReportingSerializer class (and the underlying subclasses of XstreamSerializer and XstreamShortSerializer provided by the serialization.xstream module is to provide an abstraction on top of a configured Xstream instance to consistently serialize objects for OpenMRS, and to properly handle cases of Hibernate proxies and other known issues within OpenMRS-managed objects.

If we are serializing with a particular instance (eg. the ReportingSerializer), we need to be deserializing with the same one (and vice-versa). If there are errors that happen during the serialization/deserialization process using the ReportingSerializer, then these are bugs that need to be reported and addressed. Trying other random serializer instances is not a solution that I think will be effective.

Mike

@mseaton Sure, of course. I have just been through two different processes in this thread. But yes, to be 100% clear when I serialize with ReportingSerializer I deserialize with is as well. Looking at the XML it produces with ReportData instances, it is clear that it is almost there. I am just not on top of it enough to figure out immediately what’s happening with the LinkedHashMap problem.

@dkayiwa, that’s tricky. I don’t serialize it in a unit test but within OpenMRS itself. I hack the two lines somewhere where the ReportData instance is at hand, print the XML to a file and… pass you that file along :wink: Always a bit of a pain, because I need to rebuild the module, install it… etc etc. The whole point of all this, was to obtain a full real-world ReportData object within a unit test (hence the unmarshalling) to speed up development of the routines actually “tested” through it (a report’s rendering function in my case).

The fact is also that the workaround outlined in my first post (using an alias) is enough for what I am doing. i just got curious, and yes it would make things better if I could get rid of those proxies annotations.

@mksd, I have added your sample xml and code to try to reproduce this problem here:

You can check out this module and run this unit test to check things out.

The primary take-away here was that I had to exclude the “encounters” data set definition, which refers to a class which it seems you wrote (org.openmrs.module.mksreports.dataset.definition.PatientHistoryEncounterAndObsDataSetDefinition). So my first guess is that the problem is in this class.

Mike

Thanks a lot Mike. I will get back to you about this as soon as I can spend some more time on it.