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);
Related
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());
I have a web project with 2 Java Entities(Lets Say E1,E2) like how mybatis and VO works.
Object structure:
class E1{
String a;
.... n other data members
E2 e2;
}
class E2{
String b;
.... n other data members
}
Is it possible to make a single class in Android project, i.e.
class E1 {
String a;
String b; //This Data member belongs to class E2
}
and parse it with the help of a framework (like Jackson) or I have to write a custom class for that?
My JSON Data will look like this:
{
"E1": {
"a": "some data",
.
.
.
"E2": {
"b": "some data",
.
.
other data
}
}
}
Is there any API which can do this?
I asked this because with my web Application its not just 2 Class but atleast 10 interconnected class and I am not Using them in my android app. So don't wanna replicate the same classes in android app.
Also if you can suggest any other possible way.
It would be a very bad design practice/approach, making things very difficult to debug, error prone and not future proof (think about it, what if you add to one of the 10 classes a field that conflict with another class' field?).
Anyway, if you still want to trick your way out of the correct approach that would be to have 10 classes, I am not aware of any lib that provides you with this feature. You could parse json ==> 10 Java Map, then merge the 10 Map through the Map::putAll method and finally pass the obtained Map that contains all the objects to Jackson.
Your best bet is to use #JsonAnySetter annotation from Jackson library on the receiver POJO.
public class E1{
public String a;
private Map<String, Object> paramMap = new HashMap<>();
#JsonAnyGetter
public Map<String, Object> getParamMap() {
return paramMap;
}
#JsonAnySetter
public void setParamMap(String s, Object o) {
paramMap.put(s, o);
}
}
This will put every unimplemented attributes in a HashMap.
In combination with #JsonAnyGetter, the serialization of the receiver POJO will give the same result as the JSON input.
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);
}
}
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?
I'm parsing an XML document that has nodes like the following:
<objects>
<dog>
<data1>...</data1>
<data2>...</data2>
<data3>...</data3>
</dog>
<cat>
<data1>...</data1>
<data2>...</data2>
<data3>...</data3>
</cat>
</objects>
The elements data1, data2, data3 are always consistent. Only the parent tag varies. In my object model I have a single Object which represents all of these cases. How can I get JAXB to handle this case without knowing in advance the name of the element?
#XMLAnyElement matches all the objects but doesn't create an object of the appropriate type; I get a list of Node objects instead of my object. My object currently looks something like:
public class MyObject {
protected String otherData;
#XmlAnyElement
#XmlElementWrapper(name = "objects")
protected List<MyChildObject> childObjects;
}
public class MyChildObject {
protected String data1;
protected String data2;
protected String data3;
}
Any ideas how to handle this case short of changing the incoming XML format to use <object type="dog"> elements?
If the name is truely dynamic, then I don't think JAXB will help you these. If you have a defined number of various element names then you could use inheritance like the other post suggested. If the number of elements and names is unknown I would recommend using something like this:
#XmlMixed
#XmlAnyElement
public List<Object> getObjects() {
return objects;
}
This would bring the element is a just a DOM element. You could then use JAXB a second time to go from each of the elements into your custom type.
That would be if you had to use JAXB. You might find it easier to just use the SAX or DOM APIs directly for data like this. JAXB is really intended for well defined data that can be represented as a schema.
You can use inheritance:
#XmlRootElement(name = "dog")
public class MyDogObject extends MyChildObject {
//nothing here
}
#XmlRootElement(name = "cat")
public class MyCatObject extends MyChildObject {
//nothing here
}
This way it lets you deal with the same object type, MyChildObject, yet flexibly control the XML structure. Another benefit is that should you define specific dog/cat XML nodes in the future - they can be mapped on that corresponding subclass but not the other, as expected.