Serializing a part of object graph
I have a problem regarding Java custom serialization. I have a graph of objects and want to configure where to stop when I serialize a root object from client to server.
Let's make it a bit concrete, clear by giving a sample scenario. I have Classes of type
Company
Employee (abstract) Manager extends Employee Secretary extends Employee Analyst extends Employee ProjectHere are the relations:
Company(1)---(n)Employee Manager(1)---(n)Project Analyst(1)---(n)ProjectImagine, I'm on the client side and I want to create a new company, assign it 10 employees (new or some existing) and send this new company to the server. What I expect in this scenario is to serialize the company and all bounding employees to the server side, because I'll save the relations on the database. So far no problem, since the default Java serialization mechanism serializes the whole object graph, excluding the field which are static or transient.
My goal is about the following scenario. Imagine, I loaded a company and its 1000 employees from the server to the client side. Now I only want to rename the company's name (or some other field, that directly belongs to the company) and update this record. This time, I want to send only the company object to the server side and not the whole list of employees (I just update the name, the employees are in this use case irrelevant). My aim also includes the configurability of saying, transfer the company AND the employees but not the Project-Relations, you must stop there.
Do you know any possibility of achieving this in a generic way, without implementing the writeObject, readObject for every single Ent开发者_如何学编程ity-Object? What would be your suggestions?
I would really appreciate your answers. I'm open to any ideas and am ready to answer your questions in case something is not clear.
You can make another class (a Data-Transfer-Object) where you have only the fields you want to transfer.
A way of custom serialization is implementing Externalizable
I would say the short answer to your question is no, such varied logic for serialization can't be easily implemented without writing the serialization yourself. That said an alternative might be to write several serializer/deserializer pairs (XML, JSON, whatever your favorite format, instead of standard yusing the built in serialization). and then to run your objects through those pairs sending some kind of meta-information preamble.
for example following your scenarios above you may have these pairs of (de)serialization mechanisms
- (de)serializeCompany(Company c) - for the base company information
- (de)serializeEmployee(Employee e) - for an employee's information
- (de)serializeEmployee(Company c) - the base information of employees in a company
- (de)serializeRelationships(Company c) - for the project relationships
For XML each of these can generate a dom tree, and then you place them all in a root node containing metainformation i.e.
<Company describesEmployees="true" describeRelationships="false">
[Elements from (de)serializeCompany]
[Elements from (de)serializeEmployee(Company c)]
</Company>
One potential "gotcha" with this approach is making sure you do the deserialization in the correct order depending on your model (i.e. make sure you deserialize the company first, then the employees, then the relationships). But this approach should afford you the ability to only write the "actual" serialization once, and then you can build your different transport models based on compositions of these pieces.
You could take an object swizzling approach where you send a "stub" object over the wire to your client.
Pros
- The same object graph is logically available client-side without the overhead of serializing / deserializing unnecessary data.
- Full / stub implementations can be swapped in as necessary without your client code having to change.
Cons
- The overhead in calling getters which result in dynamically loading additional attributes via a call to the server is hidden from the client, which can be problematic if you do not control the client code; e.g. An unwitting user could be making an expensive call many times in a tight loop.
- If you decide to cache data locally on the client-side you need to ensure it stays in-sync with the server.
Example
/**
* Lightweight company stub that only serializes the company name.
* The collection of employees is fetched on-demand and cached locally.
* The service responsible for returning employees must be "installed"
* client-side when the object is first deserialized.
*/
public class CompanyStub implements Company, Serializable {
private final String name;
private transient Set<Employee> employees;
private Service service;
public Service getService() {
return service;
}
public void setService(Service service) {
this.service = service;
}
public String getName() {
return name;
}
public Set<? extends Employee> getEmployees() {
if (employees == null) {
// Employees not loaded so load them now.
this.employees = server.getEmployeesForCompany(name);
}
return employees;
}
}
精彩评论