How do I convert XML into a java value object? - java

What kind of open-source libraries are available to convert XML into a java value object?
In .Net, there is a way to easily do this with xml serialization and attributes. I would imagine there some parallel in java. I know how to do this with a DOM or SAX parser, but I was wondering if there was an easier way.
I have a predefined XML format that looks something like this.
<FOOBAR_DATA>
<ID>12345</ID>
<MESSAGE>Hello World!</MESSAGE>
<DATE>22/04/2009</DATE>
<NAME>Fred</NAME>
</FOOBAR_DATA>
In .Net, I can do something like this to bind my object to the data.
using System;
using System.Xml.Serialization;
namespace FooBarData.Serialization
{
[XmlRoot("FOOBAR_DATA")]
public class FooBarData
{
private int _ID = 0;
[XmlElement("ID")]
public int ID
{
get { return this._ID; }
set { this._ID = value; }
}
private string _Message = "";
[XmlElement("MESSAGE")]
public string Message
{
get { return this._Message; }
set { this._Message = value; }
}
private string _Name = "";
[XmlElement("NAME")]
public string Name
{
get { return this._Name; }
set { this._Name = value; }
}
private Date _Date;
[XmlElement("DATE")]
public Date Date
{
get { return this._Date; }
set { this._Date= value; }
}
public FooBarData()
{
}
}
}
I was wondering if there was a method using annotations, similar to .Net or perhaps Hibernate, that will allow me to bind my value object to the predefined-XML.

I like XStream alot, especially compared to JAXB - unlike JAXB, XStream doesn't need you to have an XSD. It's great if you have a handful of classes you want to serialize and deserialize to XML, without the heavy-handed-ness of needing to create a XSD, run jaxc to generate classes from that schema, etc. XStream on the other hand is pretty lightweight.
(Of course, there are plenty of times where JAXB is appropriate, such as when you have a pre-existing XSD that fits the occasion...)

JAXB allows you to convert an XML Schema (XSD) file into a collection of Java classes. This may be more "structured" than the XMLEncoder/Serializable approach that Andy's (excellent, by the way) answer provides.

Java has an XMLEncoder that you might be able to use to serialize an object to XML. The only requirement is that your object implements "Serializable."
Here's an example:
http://www.developer.com/java/web/article.php/1377961

JiBX is another option.
For more opinions on Java-to-XML data binding, see XML serialization in Java?

Related

How do I convert JSON to Obj for InputText

I have list of json List<String> and each string have json like this {"id":"22","name":"Name","order":"1"} And I want to fetch it to list of input box then when I save it I
I want to take all changing and convert them to JSON
#ManagedBean
#ViewScoped
public class LiveStreamController extends ProductController implements Serializable {
private static final long serialVersionUID = 5037909267669512508L;
private static final Logger LOGGER = LoggerFactory.getLogger(LiveStreamController.class);
private LiveStream liveStream;
....
}
public class LiveStream extends Product implements Serializable {
private List<String> jsons = new ArrayList<>();
...
}
and I wanna read it
<c:forEach items="#{liveStreamController.liveStream.jsons}"
var="json ">
<h:outputFormat value="#{json.name}" />
<p:inputText value="#{json.order}" />
</c:forEach>
Effectively you are not asking a JSF question but an EL question since the #{json.name} expression is just that... Expression Language.
About the 'issue'...
If the value of the var="json" is a String, which it in your case is, it will be resolved by the basic resolvers that will for sure know about a String. An attempt will be made to call the property name on the string via a getName() which obviously does not exist, resulting in a Property 'name' not found on type java.lang.String. All of this is also explained in Property 'someproperty' not found on type java.lang.String. Therefor using it like in your example will not work
About the solution(s) for reading...
There are basically 3 solutions for reading the JSON and displaying it
New String EL Resolver
You could create a custom EL resolver and put it first in the order of resolvers so that for every string that needs to be resolved checks if it is effectively JSON (minimally parsing it) and if you think it IS JSON, then parse the JSON fully and read/interpret the EL and try to apply it to the JSON object. All this is going to be rather expensive as far as I can see (but BalusC might have a different idea)
New JSON EL Resolver
The second, a little better solution, is converting the String to a JSON object of you choice (there are many in Java, including a standardized one, 'JSON-P', since Java EE 7 and a newer version for Java EE 8). There is as far as I know no default included EL resolver that knows how to handle these in EL, but examples exist for the non standardized EL formats No idea how this would perform though, testing is knowing.
Existing EL Resolver
The third option is to convert the JSON to Normally strongly typed objects and then have the normal existing resolvers act on them. This can be Default existing java types like Map, List, String and other normal values, but it could even be more strongly typed like Person , Order, Converting JSON Strings to strongly typed object was a feature that existed in the non-standardized JSON libraries but not in default Java(EE) until Java EE 8, JSON-B. This has an additional advantage that code completion works and validations work in an IDE
About the solution(s) for writing...
Since you do seem to wanting to write results back to the JSON String (you have an input in your example), the first solution for reading is very hard to extend to writing, and if possible, it would totally break the advantage you seem to want to get of not writing code to be able to use this. The second and third solution both might work but the third has the advantage that input validations can be implemented, like bean validation and more.
create a class:
public class JsonWrapper {
JsonObject o;
public JsonWrapper(String s) {
this(new JsonParser().parse(s).getAsJsonObject());
}
public JsonWrapper(JsonObject o) {
this.o = o;
}
public String getJsonText() {
return o.getAsString();
}
public DataWrapper get(String field) {
return new DataWrapper(field);
}
public class DataWrapper {
String field;
public DataWrapper() {
}
public DataWrapper(String field) {
this.field = field;
}
public String getData() {
return o.get(field).getAsString();
}
public void setData(String s) {
o.add(field, new JsonPrimitive(s));
}
}
}
convert your live stream to object like this:
List<JsonWrapper> jwList = s.stream().map(s1 -> new JsonWrapper(s1)).collect(Collectors.toList());
setter & getter
Use it in xhtml
<c:forEach items="#{liveStreamController.liveStream.jwList}" var="jw">
<h:outputFormat value="#{jw.get('name').data}" />
<p:inputText value="#{jw.get('order').data}" />
</c:forEach>
Use modified data with:
List<String> jsonList = jw.stream().map(JsonWrapper::getJsonText).collect(Collectors.toList());

Serializing a POJO based on a runtime whitelist of properties

Is it possible to serialize a whitelisted subset of a POJO's properties (where the whitelist is known only at runtime) using Jackson?
All the solutions I know of so far (Views, #JsonIgnoreProperties etc.) are static, compile-time solutions.
Further, my backend returns results in the following format:
{
"outcome": "SUCCESS", // an enum
"message": "Success.", // a message for the developer
"result": {
// Some result that's different for each call
}
}
So I am looking for a solution that can be applied to only parts of the object graph (like the contents of the result property).
You probably want to look at #JsonFilter.
See this tutorial on serializing only fields that meet some criteria which includes details of this, and a couple of other methods.
For completeness
#JsonFilter("pojo-filter")
class Pojo {
public int foo;
}
FilterProvider filters = new SimpleFilterProvider()
.addFilter("pojo-filter", new SimpleBeanPropertyFilter() {
#Override
protected boolean include(PropertyWriter writer) {
return "foo".equals(writer.getName())
? Random.nextBoolean()
: true;
}
});
new ObjectMapper().writer().filters(filters).write(new Pojo());
Globally you can use ObjectMapper.setFilterProvider

Include enum type to serialize and deserialize with jackson

I have a simple enum I'd like to serialize and deserialize. The class looks like this:
public enum TipusViatge {
OCI,
NEGOCIS,
FAMILIA;
#Override
public String toString() {
return name().toUpperCase();
}
}
The thing is, I send it via a restful call and the receiving side may receive any type (so it only knows it will receive Object). So Jackson should be able to figure out the type of the argument to deserialize it.
Is it possible to do so? I was thinking that including the class name in the resulting json should allow Jackson to figure out the type, but I've been unable to do so.
I have worked over this problem for a while.
1st you could deserialize your json with Map<String, Object>. It alway works; you get standard types (your enumeration will be readed as plain string).
2nd in general case you alway know what kind of object you read. This is top-level object and you can set it to Jackson mapper: mapper.readerFor(cls).readValue(json). In case of your enumeration is a part of this cls object, then Jackson knows the type and just read value and parse to it.
3rd you actually could have multiple objects for one json string. I am talking about inheritance. And you could look at #JsonTypeInfo in Jackson documentation.
4th imagin that you read a json source and do not know what you read. In this case, you could ask Jackson to write marker at the beginning of the object. Just like you asking about class name. I think it relates to #JsonRootName. You can look on it here: Jackson JSON Deserialization with Root Element
I think that it is clean now how to work with objects in Jackson. I mean that we know how to tell Jackson what element we want to deserialize. Now we have one problem: how to serialize json -> our enumeration.
5th this is not a problem and works out of the box. Jackson uses name() method to serialize enumeration, and valueOf() to deserialize. You can look at it closer in EnumDeserializer in Jackson.
6th I do not like this behaviour, becuase it is case-sencitive. I faced with situation that when people write json string manually, the use lower-case and cannot deserialize it. Moreover, I belive, that writing enumeration constants directly to the json file is a bad practise, because If I want to refactor names of the enumeration, all existed json string should be modified as well (brrr). To solve thiese issues, I do following trick:
1. Implement EnumId interface with default implementation of parseId(String id) with using getId() to identify enumeration constants and using ignore case for compare.
1. I add id field to the enumeration
2. Add getId() - for serialization
3. Add parseId(String id) - for deserialization
4. Add new module in Jackson ObjectMapper with my customer serializer (it
should use `getId()` instead of `name()`).
if (enumId != null) {
generator.writeString(enumId.getId());
}
And tell Jackson how to deserialize this enum. Here this is dificult situation, becuase in different sources, Jackson use different deseriaization hierarchy and just adding another module to ObjectMapper with custom deserialize (just like in 4.) will not be working with all situations. To solve this problem, I found out that we could add #JsonCreator to parseId(String id) method in enumeration and Jackson will be using it in all situation.
I think that is all about this topic. I give you a code example to make it more clearly (it is better to write once, then explain twice):
public interface EnumId {
String name();
default String getId() {
return name().toLowerCase();
}
static <T extends Enum<?> & EnumId> T parseId(Class<T> cls, String id) {
T res = parseId(cls.getEnumConstants(), id, null);
if (res != null) {
return res;
}
throw new EnumConstantNotPresentException(cls, id);
}
static <T extends EnumId> T parseId(T[] values, String id, T def) {
for (T value : values) {
if (id != null ? id.equalsIgnoreCase(value.getId()) : value.getId() == null) {
return value;
}
}
return def;
}
static <T extends EnumId> T get(T value, T def) {
return value != null ? value : def;
}
}
public enum TipusViatge implements EnumId {
OCI,
NEGOCIS,
FAMILIA;
#JsonCreator
public static TipusViatge parseId(String id) {
return EnumId.parseId(TipusViatge.class, id);
}
}

Java / JPA - dynamically setting properties on an object / entity

I'm relatively new to Java and would appreciate any help on this!
I have an XML file full of products like this:
<product>
<title>Product Title</title>
<colour>Red</colour>
</product>
And I have a JPA Entity like this:
#Entity
public Product extends Model {
public String title;
public String colour;
}
And I can happily parse the XML into my JPA object like this:
public void parseElement(String elementName, String elementValue) {
if (elementName == "title") {
product.title = elementValue;
}
else if (elementName == "colour") {
product.colour = elementValue;
}
}
However the problem is that there are over 50 fields per product, not just the two. I could write a 50+ clause if-else statement, but thought it'd be worth checking here for better alternatives first!
As the XML element names directly match to the property names in the Product class, I thought something like this would be perfect:
public void parseElement(String elementName, String elementValue) {
product[elementName] = elementValue;
}
But Java doesn't like that notation. Is there something else I can do that would achieve a similar result, or do I have to suck it up and write a collosal if-else statement?
Any help would be greatly appreciated.
Cheers!
Java knows a lot frameworks for XML processing. Also some libs for serializing/deserializing objects to/from xml. I would recommend to have a look at JAXB.
You'll have to use reflection (error handling ommitted).
product.getClass().getDeclaredField(elementName).set(product, elementValue);
But I stand by others who have suggested XStream. It's a much cleaner way to parse XML files.
Another option would be to use something like XStream to serialize/deserialize your entities to and from xml.
Checkout BeanUtils.setProperty
http://commons.apache.org/beanutils/
Hmm, perhaps not the best solution but you could also simply use a map:
public Product extends Model {
public Map<String,String> myMap = new HashMap<String,String>();
}
and:
public void parseElement(String elementName, String elementValue) {
product.myMap.put(elementName,elementValue);
}
You could use the very well known Apache Commons Lang and then do something like this:
FieldUtils.writeField(myObject, fieldName, fieldValue);

morphia handle bad data

Let's say I have some json like this in mongo:
{"n":"5"}
and a java class like this:
#Entity
public class Example {
Integer n;
}
This works (I know that the json should store the value as an int not a string but I don't control that part).
Now when I have data like this morphia throws:
{"n":""}
I'm looking for a workaround (the behavior I'd like is for empty string to be treated same as null).
The only workaround I have so far is:
public class Example {
String n;
public Integer getN() {
return NumberUtils.isNumber(n) ? NumberUtils.createInteger(n) : null;
}
}
But I'm hoping for some way to hang an annotation on the Integer property that customizes the deserialization behavior.
So I asked this on the morphia google group and I thought I'd share the answer. Using the lifecycle annotation #PreLoad allows me to modify the DBObject before conversions into POJO takes place. So this should do it:
#PreLoad void fixup(DBObject obj) {
if (StringUtils.isEmpty(obj.get("n"))) {
obj.put("n",null);
}
}

Categories

Resources