问题
I am developing a RESTful web service.
I have a bunch of entity classes (mostly JPA entities, but also other beans).
There are gazillions of object mapping, serialization, binding and whatnot libraries out there. I'm looking for a one that will enable me to:
Serialize the entities to XML and JSON
Serialization MUST support using getters, not only object fields.
It MUST support views. By views I mean a way to specify a subset of the properties of the entity, which are to be serialized. For an example, see Retrieving Partial Resources in the Yahoo! Social Platform API. I also don't want it to recurse indefinitely deep:
The view should define a) properties to be exposed in the entity and b) a view for each of them (if they're entities themselves).
For example, the entity
Person
might have viewsfull
andsimple
. When requesting asimple
view of aPerson
, only propertiesid
,firstName
andlastName
would be serialized. When requesting afull
view, the propertiesmother
andfather
(which arePersons
themselves) would also be serialized, but only with thesimple
view (so it wouldn't recurse to grandparents).The JSON serialization MUST be "natural", i.e. what would make sense in Javascript. That means I want proper integers, booleans and nulls and I don't want extra object wrappers or something that tries to capture the entire XML Infoset.
For XML, it MUST be possible to configure the serialization to use empty elements for serializing nulls, not XML Schema's
xsi:nil="true"
attributes.Additionally, arrays must be serialized using nested elements so it's possible to differentiate between empty arrays and properties which aren't present in a given view:
Undefined
friends
property (not present in view):<person> </person>
Empty array, person has no friends:
<person> <friends></friends> </person>
Deserialize XML and JSON and merge the data into existing entities
Now this is the difficult part. The next two are connected:
Deserialization of partial objects MUST be possible (analogous to views, but which properties are present is not defined in advance).
Merging into existing properties, not creating new ones.
This is a bit tricky to explain. Most libraries deserialize into Java objects, but at that time, the distinction between an undefined property and a null/empty property (as it exists in Javascript/JSON, XML as described above, PHP, ...) is lost.
When being given this XML:
<person> <lastName>Bon Jovi</lastName> <friends></friends> </person>
and being passed a
Person
object, the library should:person.setLastName("Bon Jovi"); person.setFriends(new ArrayList());
It shouldn't touch the entities
firstName
and/or clear it'sfather
, for example.Of course with lists this should be more complex. I would expect to configure an id property, by which it would decide whether to update the nested entity or create a new one.
These updates/patches are a reason why the library MUST NOT just return a DTO: because at that time a
null
might either mean "unset" or "do nothing".
Well, that's it. I've been saying "MUST" a lot, I realize now :) The library needn't actually provide that, but there must be a way to add the functionality in a clean fashion (= not in a way that would make rewriting everything easier).
回答1:
I don't know of existing libraries that do everything you need, but, assuming you're going to need to implement something:
Writing XML or JSON based on bean properties is pretty simple:
- Look at using Apache Commons BeanUtils to be able to get all property values (http://commons.apache.org/beanutils/). In particular, the PropertyUtils class.
- Use BeanUtils recursively to walk your entire object graph - be careful of cycles - you'll need a Set or something on the side to keep track of what you've already seen
- XML: Look at XMLEncoder - it works using JavaBean properties to create XML
For reading: One approach might be to use existing libs (for JSON or XML) to create objects then deal with merging the properties between objects. The XMLDecoder class can read bean XML (assuming you create it using XMLEncoder). The tricky part to this approach is to know when a value has been "set" to null vs just not been set in the XML. This approach also has the extra overhead of creating a bunch of new objects.
Otherwise, reading JSON or XML is a little trickier, but not too bad
- I assume you already have some means of indexing the objects you want to merge into (like a Map of some sort)
- I assume you already have some means to know which property is the key that uniquely identifies an object (I assume last name is just to get the point across, as it would make a bad key)
- XML: For this type of use, I'd recommend a SAX reader for the xml - you'll need a stack to keep track of which objects you're adding data to. The SAX reader tells you what tags are seen and then gives the values for those tags. You could also use XML pull here, which tends to be a little faster
- JSON: take a look at some of the open source JSON libraries and do some tweaking. JSON is pretty simple to parse, and these tools tend to be pretty small so this shouldn't be a big deal. Alternatively, you could write an ANTLR (or other generator) parser to read the JSON and do with it as you like.
回答2:
For what it is worth, Jackson has partial update out-of-the-box:
ObjectMapper mapper = new ObjectMapper();
Bean existing = ...;
mapper.updatingReader(existing).readValue(jsonSource);
and it can also convert compatible types (similar to serializing to JSON, reading back to different type).
For XML part you can use JAXB, although it can only complete binding as far as I know.
来源:https://stackoverflow.com/questions/731989/partial-bean-serialization-and-deserializationmerging