In one of my applications I'm using thegamesdb.net's APIs to download the cover images of some games. To get informations from XML files I'm trying to implement Simple XML library to deserialize.
This is my example query result (name=Splatterhouse,platform=TurboGrafx 16):
<?xml version="1.0" encoding="UTF-8" ?>
<Data>
<Game>
<id>3776</id>
<GameTitle>Splatterhouse</GameTitle>
<ReleaseDate>01/01/1990</ReleaseDate>
<Platform>TurboGrafx 16</Platform>
</Game>
</Data>
And this is how I implemented my classes in java:
#Root
public class Data{
#ElementList(inline=true)
private List<Game> list; //This is correct, it's only in the example that I've only one result.
public List<Game> getGames(){
return list;
}
}
#Root
public class Game{
#Element
private int id;
#Element
private String GameTitle;
#Element(required=false)
private String ReleaseDate;
#Element(required=false)
private String Platform;
public String getTitle(){
return GameTitle;
}
public int getId(){
return id;
}
}
To read from the xml file, I call the serializer:
String xml = getXmlFromUrl(URL);
Serializer serializer = new Persister();
Data data = serializer.read(Data.class, xml);
What's wrong?
UPDATE: It came out that the tutorials on Simple Site are a bit incomplete since they don't set properly annotations, these annotations solved all my issues, plus all the warnings are "normal", it's simple stating that jaxp doesn't exist on android and is going to use other tools (XmlPullParser?).
This is my proper code:
#Root(name = "Data")
public class Data {
#ElementList(inline = true, required = false)
private List<Game> list = new ArrayList<Game>();
public List<Game> getGames() {
return list;
}
}
#Root(name = "Game", strict = false)
public class Game {
#Element(name = "id")
private int id;
#Element(name = "GameTitle")
private String GameTitle;
public String getTitle() {
return GameTitle;
}
public int getId() {
return id;
}
}
You could also use JAXB to (de)serialize XMLs (or to (un)marshal as they call it). It comes with java - so there's no need for an additional library. Here you have an example.
I believe you need to declare your classes in their own files (Data and Game), or mark them as static. If you declare them as inner classes they can't be constructed by the library because they can only exist within an instance of the parent class.
It's nothing to do with your XML, it seems like something is wrong with the library you're using (not being able to create a new instance of a certain class). Consider re-downloading it and adding it to your project again.
The tutorials on SimpleXML website show an incomplete and inaccurate use of annotations.
I updated my code, check at the bottom of my answer to get the proper implementation. You're strictly adviced to use name="Name" in your annotations, and if you don't need all the Elements/Attributes use strict=false on the class and required=false on your elements or attributes.
Related
I have the following #Builder - annotated class:
#Data
#Builder(access = AccessLevel.PUBLIC)
#Entity
public class LiteProduct
{
// Minimal information required by our application.
#Id
private Long id; // Unique
private String name;
private Long costInCents;
private String type;
// Model the product types as a Hash Set in case we end up with several
// and need fast retrieval.
public final static Set<String> PRODUCT_TYPES = new HashSet<>
(Arrays.asList("flower", "topical", "vaporizer", "edible", "pet"));
// Have to allow creation of products without args for Entities.
public LiteProduct()
{
}
public LiteProduct(#NonNull final Long id, #NonNull final String name,
#NonNull final String type, #NonNull final Long costInCents)
{
if(!PRODUCT_TYPES.contains(type))
{
throw new InvalidProductTypeException(type);
}
this.name = name;
this.id = id;
this.costInCents = costInCents;
}
Whenever I want to use the builder class that Lombok is purported to give me, despite the fact that the IDE seems to detect it just fine:
I get a compile-time error about its visibility:
I have looked at some workarounds such as this or this, and they all seem to imply that my problem ought to already be solved automatically and that Lombok by default produces public Builder classes. This does not seem to be implied from my output, and does not happen even after I put the parameter access=AccessLevel.PUBLIC in my #Builder annotation in LiteProduct. Any ideas? Is there something wrong with the fact that this class is also an #Entity? Something else I'm not detecting?
// Edit: I determined that when I move the class in the same package as the one I am calling the builder pattern from, it works just fine. This is not an #Entity issue, but a package visibility issue which based on what I'm reading should not be there.
The problem was that I was using the following line of code to create an instance of LiteProduct:
return new LiteProduct.builder().build();
instead of:
return LiteProduct.builder().build();
which is what the #Builder annotation allows you to do. Clearly builder() is like a factory method for Builders that already calls new for you.
Working on a REST client that calls another server which returns the following object:
public class ObjectOriginal {
private int id;
private String name;
// constructor/getters/setters
}
I need to obfuscate the id. To do so I'm using an already existing service that transforms the id into a unique generated String so that the person calling my service doesn't know the REAL id but can still request info about it with the unique string.
So I'm basically trying to return to the caller this object:
public class ObjectNew {
private String id;
private String name;
// constructor/getters/setters
}
Do I need to have a copy of ObjectOriginalDTO + create a ObjectNew DTO + create a mapper to go from one to the other.
Or can I configure Jackson to deserialize the id field as a String and not an int?
You can do this using your own Serializer/Deserializer.
You have to implement your Serializer/Deserializer that will extends respectively BeanSerializerModifier/BeanDeserializerModifier and configuring your Module with them for instance Or use the annotation base solution as explained in this tutorial, there are plenty of references on the web for such a thing. then you'll have more controlle over the way to map your id.
If you don't want to have custom deserializer you can have:
public class ObjectNewDto {
private String id;
private String name;
// constructor/getters/setters
}
and another object:
public class ObjectOriginal {
private int id;
private String name;
// construxtor/getters/settes
}
Now after validating ObjectNewDto you can map it via your obfuscator service into ObjectOriginal , then validate this Object original and so on...
So, I am making a project that has a class Save which contains List of GameField class. To save "Save" to DB(with Hibernate).
I'm using JSON and it works pretty good, but it is parsing all fields from GameField , which makes Json String about 600 characters long. Beacuse it is so long , I am getting org.hibernate.exception.DataException which says that size of String is too big.
I was trying to increase avalible space in database to properly store that big String but it didn't work.
So i have found #JsonIgnore and #JsonIgnoreProperties annotations to stop Json from parsing few fields. It worked but only for Save class not for GameField.
Is it possible to prevent Json from parsing any fields that i want?
Here is Save class:
public class Save {
private List<GameField> fields;
// getters setters , etc.
}
Game Field:
public class GameField {
#JsonIgnore
private boolean isSet;
private char fieldSign;
#JsonIgnore
private final int numberInArray;
}
Fragment of parsing code:
public String getEncodedSaveInString(Save save) throws JsonProcessingException {
mapper = new ObjectMapper();
return mapper.writeValueAsString(save);
}
Piece of User class
#Entity
public class User {
#Column(name = "Save")
private Save save;
}
Is there a solution?
Okay, so i found a solution. Only thing i needed to do was putting #JsonIgnore annotation above field setter. Placing it above field itself was not working
Working sample of code:
private Foo foo;
#JsonIgnore
public void setfoo(Foo newFoo){ this.foo = newFoo;}
That works fine :)
I got a requirement that I have to map my xml to java object without parsing it, but the problem is like that in xml tag names would be same, for example,
<response>
<employee>
<name>Sharique</name>
<name>24</name>
<name>India</name>
</employee>
</response>
and class would be like this
public class Employee{
private String empName;
private int age;
private String country;
//getters and setters
}
Please help!!
If it can be done using spring than that would be very nice
If you leverage EclipseLink MOXy as your JAXB (JSR-222) provider then you can use our #XmlPath extension for this use case.
#XmlAccessorType(XmlAccessType.FIELD)
public class Employee{
#XmlPath("name[1]/text()")
private String
#XmlPath("name[2]/text()")
private int age;
#XmlPath("name[3]/text()")
private String country;
//getters and setters
}
For More Information
I have written more about the #XmlPath extension on my blog:
http://blog.bdoughan.com/2010/07/xpath-based-mapping.html
Not required, As per javax.xml.bind.annotation you can do like below,
#XmlElement(name="name")
private String empName;
So now the empName in your java class will be mapped to name attribute in your XML.
and your XML should not have 'name' as name for all attributes. it cannot differentiate, so you need to use different tags in your XML for other elements like age and so on ans map accordingly as i stated above in your POJO.
You really got some weird XML there. I thing data binding will not help in this case, but you can do it with data projection. (Disclosure: I'm affilited with that project)
public class ParseResponse {
public interface Employee {
#XBRead("./name[1]")
String getName();
#XBRead("./name[2]")
int getAge();
#XBRead("./name[3]")
String getCountry();
}
public static void main(String[] args) {
List<Employee> employees = new XBProjector().io().url("res://response.xml").evalXPath("//employee").asListOf(Employee.class);
for (Employee employee:employees) {
System.out.println(employee.getName());
System.out.println(employee.getAge());
System.out.println(employee.getCountry());
}
}
}
this program prints out
Sharique
24
India
if you fix the XML closing tags.
Every book on REST uses <atom:link href="..." rel="..."> to define Hypermedia in RESTful apps; but Jersey (with the use of JAXB) do not seems to have this support.
I've tried #XmlSchema in package-info.java as explained here; I've also tried extendingNamespacePrefixMapper as explained there. But none works and output this at best:
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<customer xmlns:ns2="http://www.w3.org/2005/Atom">
<first-name>Roland</first-name>
<ns2:link href="/customer/1" rel="self" />
</customer>
Using namespace and, as a result, Atom, seems impossible in Jersey. I've miss something?
ps. I'me using a XSD to generate #XmlElement classes, and, for the moment, I create my own Link class. Is there a schema or a JAR to do that (jersey-atom maven dependency uses rome but without any help)
(Assuming that you are not concerned with the namespace prefix and just want to create the links)
Here is my approach to creating the links. In my resource class (the jersey rest service), I return a java object (below "Person"), whose class is decorated with jaxb annotations. One of the properties returns atom link objects.
#XmlRootElement(namespace = Namespace.MyNamespace)
public class Person implements Serializable {
private AtomLinks links = null;
#XmlElement(name = "link", namespace = Namespace.Atom)
public AtomLinks getLink() {
if (this.links == null) {
this.links = new AtomLinks();
}
return this.links;
}
..
}
#XmlAccessorType(value = XmlAccessType.NONE)
public class AtomLinks extends ArrayList<AtomLink> {
..
}
#XmlAccessorType(value = XmlAccessType.NONE)
public class AtomLink implements Serializable {
#XmlAttribute(name = "href")
public URI getHref() {
return href;
}
#XmlAttribute(name = "rel")
public String getRel() {
return rel;
}
#XmlAttribute(name = "type")
public String getType() {
return type;
}
#XmlAttribute(name = "hreflang")
public String getHreflang() {
return hreflang;
}
#XmlAttribute(name = "title")
public String getTitle() {
return title;
}
..
}
public class Namespace {
public final static String Atom = "http://www.w3.org/2005/Atom";
..
}
Prior to returning my object ("Person") I fill in the links, creating a self link and links to other related links. I utilize the uriInfo object that jersey injects to get the base url. If this is helpful but you would like more of the example, let me know and I will fill in the gaps.
If I am right there is an approach in Jersey to inject the links to the objects.
See: Jersey 2.9 User Guide Chapter 6.