JAXB Reading XML document - java

I'm trying to read an XML document and decode it in to Java Beans. I have the reading part settled but I run in to an issue. I'm basically trying to decode all the child nodes of the XML document, root being "catalog". How do I do this using the XMLDecoder?
XMLDecoder:
private static Book jaxbXMLToObject() {
try {
JAXBContext context = JAXBContext.newInstance(Book.class);
Unmarshaller un = context.createUnmarshaller();
Book book = (Book) un.unmarshal(new File("PATH"));
return book;
} catch (JAXBException e) {
e.printStackTrace();
}
return null;
}
I'm trying to read the following document
<?xml version="1.0"?>
<catalog>
<book id="1">
<author>Isaac Asimov</author>
<title>Foundation</title>
<genre>Science Ficition</genre>
<price>164</price>
<publish_date>1951-08-21</publish_date>
<description>Foundation is the first novel in Isaac Asimovs Foundation Trilogy (later expanded into The Foundation Series). Foundation is a cycle of five interrelated short stories, first published as a single book by Gnome Press in 1951. Collectively they tell the story of the Foundation, an institute to preserve the best of galactic civilization after the collapse of the Galactic Empire.</description>
</book>
</catalog>
And Parse it in to a Book Object
#XmlRootElement(name = "book")
#XmlType(propOrder = {"id", "price", "title", "author", "genre", "description"})
public class Book {
private int id;
private int price;
private String title;
private String author;
private String genre;
private String description;
private Date publish_date;
public Book() {
}
......
I get the error: jjavax.xml.bind.UnmarshalException: unexpected element (uri:"", local:"catalog"). Expected elements are <{}book>
How do I only access the child nodes using JAXB?
UPDATE
Catalog Class:
#XmlRootElement(name = "catalog")
public class Catalog {
#XmlElement(name = "book")
List<Book> books;
public List<Book> getBooks() {
return books;
}
public void setBooks(List<Book> books) {
this.books = books;
}
}
Book class:
#XmlAccessorType(XmlAccessType.FIELD)
public class Book {
#XmlAttribute
int id;
private int price;
private String title;
private String author;
private String genre;
private String description;
private Date publish_date;
public Book() {
}
public Book(int id, int price, String title, String genre, String description, Date publicationDate) {
this.id = id;
this.price = price;
this.title = title;
this.genre = genre;
this.description = description;
this.publish_date = publicationDate;
}
public int getId() {
return id;
}
public int getPrice() {
return price;
}
public String getTitle() {
return title;
}
public String getGenre() {
return genre;
}
public String getDescription() {
return description;
}
public Date getPublicationDate() {
return publish_date;
}
public void setId(int id) {
this.id = id;
}
public void setPrice(int price) {
this.price = price;
}
public void setTitle(String title) {
this.title = title;
}
public void setGenre(String genre) {
this.genre = genre;
}
public void setDescription(String description) {
this.description = description;
}
public void setPublish_date(String publish_date) {
this.publish_date = new Date();
}
public String getAuthor() {
return author;
}
public void setAuthor(String author) {
this.author = author;
}
public Date getPublish_date() {
return publish_date;
}
public String toJSON() {
ObjectMapper mapper = new ObjectMapper();
try {
return mapper.writeValueAsString(this);
} catch (IOException e) {
e.printStackTrace();
}
return null;
}
#Override
public String toString() {
return "Book{" +
"id=" + id +
", price=" + price +
", title='" + title + '\'' +
", genre='" + genre + '\'' +
", description='" + description + '\'' +
", publicationDate=" + publish_date +
'}';
}
}
DAO:
public class BooksDAO {
public BooksDAO() {
}
public List<Book> getBooks() {
Catalog catalog = jaxbXMLToObject();
return catalog.getBooks();
}
private static Catalog jaxbXMLToObject() {
try {
return JAXB.unmarshal(new File("PATH"), Catalog.class);
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
}

As already pointed out by JB Nizet you definitely need an enclosing Catalog object. The following is the bare minimum to be able to unmarshal the provided XML document using JAXB and extract the book from it:
public class ReadXMLUsingJAXB {
static class Catalog {
#XmlElement(name = "book")
List<Book> books;
}
#XmlAccessorType(XmlAccessType.FIELD)
static class Book {
#XmlAttribute
int id;
String author;
String title;
String genre;
int price;
Date publish_date;
String description;
}
private static Book firstBookFromXML() {
Catalog catalog = JAXB.unmarshal(new File("PATH"), Catalog.class);
return catalog.books.get(0);
}
public static void main(String[] args) {
Book book = firstBookFromXML();
System.out.println(book.id + ", " + book.author + ", " + book.title
+ ", " + book.genre + ", " + book.price
+ ", " + book.publish_date + ", " + book.description);
}
}
Some things are worth mentioning here:
The #XmlAccessorType-Annotation is not necessary with Catalog as there is only one field which is annotated with #XmlElement.
When chosing FIELD as access type all fields are taken into account regardless of their visibility unless annotated with #XmlTransient.
The book ID is an attribute in the document, so it must be declared as such using #XmlAttribute.
#XmlElement on Catalog.books was necessary to reflect the name of the book-Elements. JAXB defaults to the field (or property) name which would be books instead and thus not match the elements.
As said before the demonstration code is the bare minimum and should be changed to fit your needs (i.e. field visibility, proper constructor, getters, equals, hashCode, toString etc.)

Related

How to write an array of objects through composition in another object and how to create them in main class?

For example, there is this program where I could write in the Book object (constructor) many Authors. The errors appear only in main, but there may be some code in other classes, which should be written differently.
```
package ex1;
public class Author {
private String name, email;
private char gender;
public Author(String name, String email, char gender) {
this.name = name;
this.email = email;
this.gender = gender;
}
public String toString() {
return "Author [name=" + name + ", email=" + email + ", gender=" + gender + "]";
}
public String getName() {
return name;
}
public void setEmail(String email) {
this.email = email;
}
public char getGender() {
return gender;
}
}
```
In the photo, you can see the program requirements.
```
package ex1;
import java.util.Arrays;
public class Book {
private String name;
private Author[] authors;
private Page page;
private double price;
private int qty = 1;
public Book(String name, Author[] authors, double price) {
this.name = name;
this.authors = authors;
this.price = price;
}
public Book(String name, Author[] authors, Page page, double price, int qty) {
this.name = name;
this.authors = authors;
this.price = price;
this.qty = qty;
this.page = page;
}
#Override
public String toString() {
return "Book [name=" + name + ", authors=" + Arrays.toString(authors) + ", page=" + page + ",
price=" + price + ", qty=" + qty + "]";
}
public String getName() {
return name;
}
public Author[] getAuthors() {
return authors;
}
public double getPrice() {
return price;
}
public void setPrice(double price) {
this.price = price;
}
//....
}
```
The class page is working at least.
```
package ex1;
public class Page {
private int pageNumber, numberOfWords;
private String footnote;
public Page(int pageNumber, int numberOfWords, String footnote) {
this.pageNumber = pageNumber;
this.numberOfWords = numberOfWords;
this.footnote = footnote;
}
#Override
public String toString() {
return "Page [pageNumber=" + pageNumber + ", numberOfWords=" + numberOfWords + ", footnote=" +
footnote + "]";
}
public int getPNr() {
return pageNumber;
}
public int getWords() {
return numberOfWords;
}
public String getFoot() {
return footnote;
}
}
```
So here I would like to see that I could create a Book like this or in a similar manner:
Book b2 = new Book("Ac2", authors[authorAna, Kratos], 35);
```
package ex1;
import java.util.Arrays;
public class Main {
public static void main(String[] args) {
Author authors[] = new Author[2]; // this is a method but it doesn't work as intended
Author authorAna = new Author("Ana", "A#em.com", 'f');
Author Kratos = new Author("Kratos", "K#em.com", 'm');
authors[0] = authorAna;
authors[1] = Kratos;
Page p6 = new Page(6, 400, "He jumped into the haystack to hide from enemies");
Book b1 = new Book("Ac1", authors, 25);
//Book b2 = new Book("Ac2", authorAna, 35);
//Book b3 = new Book("God of War1", Kratos, 20);
//Book b4 = new Book("GoW2", , p6, 20, 40);
System.out.println(Kratos + "\n" + b1.toString());
//System.out.println(b2);
//System.out.println(b3);
//System.out.println(b4);
}
}
```
You can create the Author array like this. Then you would need to index the array to get the individual author object.
Author[] authors = {new Author("Ana", "A#em.com", 'f'),
new Author("Kratos", "K#em.com", 'm')};
Book b1 = new Book("Ac1", authors, 25);
If need be, you could then create a book array the same way or create a book builder.
class Book {
private String name;
private Author[] authors;
private Page page;
private double price;
private int qty = 1;
private Book() {
}
public Book(String name, Author[] authors, double price) {
this.name = name;
this.authors = authors;
this.price = price;
}
public Book(String name, Author[] authors, Page page,
double price, int qty) {
this.name = name;
this.authors = authors;
this.price = price;
this.qty = qty;
this.page = page;
}
#Override
public String toString() {
return "Book [name=" + name + ", authors="
+ Arrays.toString(authors) + ", page=" + page
+ ", price=" + price + ", qty=" + qty + "]";
}
public String getName() {
return name;
}
public Author[] getAuthors() {
return authors;
}
public double getPrice() {
return price;
}
public void setPrice(double price) {
this.price = price;
}
public static Book bookBuilder() {
return new Book();
}
public Book price(double price) {
setPrice(price);
return this;
}
public Book quantity(int quantity) {
qty = quantity;
return this;
}
public Book name(String name) {
this.name = name;
return this;
}
}
It would be used like this.
Book b = Book.bookBuilder().name("Ac2").price(40.).quantity(20);
Note that you can modify the bookBuilder method to accept any elements of the book class that would always be required. Then you can add whatever methods you need. And the other nice feature of builders is that the order you call the methods doesn't matter (except for the first one of course).

How to handle message error in Entity when using #MockMvc

I using #MockMvc test in spring controller but i have a question.
How to handle message error when MockMvc test not pass method.
Entity:
#Entity
#ApiModel(description = "All details about the Product")
public class Product {
#Id
#GeneratedValue(strategy = GenerationType.AUTO,generator = "system-uuid")
#GenericGenerator(name = "system-uuid",strategy = "uuid2")
private String id;
#NotNull(message = "name can not null")
#ApiModelProperty(notes = "The name is product")
private String name;
#ApiModelProperty(notes = "The type is product")
private String type;
#NotNull(message = "category can not null")
private String category;
private String description;
private Double prince;
public Product() {
}
public Product(String name, String type, String category, String description, Double prince) {
this.name = name;
this.type = type;
this.category = category;
this.description = description;
this.prince = prince;
}
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getType() {
return type;
}
public void setType(String type) {
this.type = type;
}
public String getCategory() {
return category;
}
public void setCategory(String category) {
this.category = category;
}
public String getDescription() {
return description;
}
public void setDescription(String description) {
this.description = description;
}
public Double getPrince() {
return prince;
}
public void setPrince(Double prince) {
this.prince = prince;
}
#Override
public String toString() {
return "Product{" +
"id='" + id + '\'' +
", name='" + name + '\'' +
", type='" + type + '\'' +
", category='" + category + '\'' +
", description='" + description + '\'' +
", prince=" + prince +
'}';
}
}
StudentController:
#RestController
#RequestMapping("/products")
public class ProductController {
#PostMapping
public ResponseEntity<ProductDto> createProduct(#RequestBody Product product) {
URI location = ServletUriComponentsBuilder.fromCurrentRequest().path("/{id}").buildAndExpand(product.getId()).toUri();
return ResponseEntity.created(location).body(productService.createProduct(product));
}
}
In above entity, I want using #MockMvc test createProduct. If name in product is null, i want show message in #MockMvc . It look like: "name can not null" . If pass, i don't want show it. Bellow my test:
#Test
public void givenProductURIWithPost_whenMockMVC_thenVerifyResponse() {
this.mockMvc.perform(post("/products")).andDo(print())
.andExpect(status().isOk()).andExpect(content()
.contentType("application/json;charset=UTF-8"))
}
I have two question:
1.How to show message "name can not null" if name in product is
null in #mockmvc.
2. If my project in 20 field in Products entity : Example: name,category.. I can test sequence field in Products or only test
one time contain all field.

Validating value objects using annotations

I've decided to use Value Objects instead of String fields in my entity and I don't know how (and if it's even possible) to validate them using JPA Annotations like #Size, #Pattern and so on.
Here is my Book entity:
#Entity
#Access(AccessType.FIELD) // so I can avoid using setters for fields that won't change
public class Book {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long bookId;
#Embedded
private Isbn isbn;
#Embedded
private Title title;
#Embedded
private Author author;
#Embedded
private Genre genre;
#Embedded
private PublicationYear publicationYear;
private BigDecimal price;
// jpa requirement
public Book() {
}
public Book(Isbn isbn, Title title, Author author, Genre genre, PublicationYear publicationYear,
BigDecimal price) {
this.isbn = isbn;
this.title = title;
this.author = author;
this.genre = genre;
this.publicationYear = publicationYear;
this.price = price;
}
public Long getBookId() {
return bookId;
}
public Isbn getIsbn() {
return isbn;
}
public Title getTitle() {
return title;
}
public Author getAuthor() {
return author;
}
public Genre getGenre() {
return genre;
}
public BigDecimal getPrice() {
return price;
}
public PublicationYear getPublicationYear() {
return publicationYear;
}
// setter for price is needed because price of the book can change (discounts and so on)
public void setPrice(BigDecimal price) {
this.price = price;
}
}
And here is my example value object - all are just using Strings.
public class Isbn {
private String isbn;
// jpa requirement
public Isbn() {
}
public Isbn(String isbn) {
this.isbn = isbn;
}
public String getIsbn() {
return isbn;
}
#Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
Isbn isbn1 = (Isbn) o;
return isbn != null ? isbn.equals(isbn1.isbn) : isbn1.isbn == null;
}
#Override
public int hashCode() {
return isbn != null ? isbn.hashCode() : 0;
}
#Override
public String toString() {
return "Isbn{" +
"isbn='" + isbn + '\'' +
'}';
}
}
Is there a simple way to validate those objects? If it was a String in my entity instead of Isbn object I could just use #Pattern to match correct Isbn and be done with it.
EDIT1: Maybe there is a better way to validate value objects than above one? I'm kinda new to this stuff so would like to know if there is a better option to validate my entities.
You can enforce validation of Object fields by utilizing #Valid annotation;
#Entity
#Access(AccessType.FIELD)
public class Book {
#Embedded #Valid
private Isbn isbn;
...
}
public class Isbn {
#Pattern(//Pattern you'd like to enforce)
private String isbn;
...
}
Then you can validate by yourself using the following;
ValidatorFactory factory = Validation.buildDefaultValidatorFactory();
Validator validator = factory.getValidator();
Set<ConstraintViolation<User>> violations = validator.validate(book);
//if set is empty, validation is OK

java - Hibernate doesn't read object from database correctly

I am having some issues according Hibernate.
I have a more or less complex object structure that I want to save / load using the Hibernate EntityManager (version 4.3.5.Final). I managed to save it, but if I attempt to read the obejct, only the PK will be read. The EntityManager's find method returns null even with the correct PK so I am using its getReference method.
I still have troubles using the correct relationship (ManyToOne and such) so I most likely made a mistake there and I guess that is causing the problem.
Anyways.
My question is: How do i persist an object structure like this using Hibernate?
Here are the POJOs i am using:
EDIT: Updated the Code
CalculationList:
#Entity(name = "calculation")
public class CalculationList implements EntityList {
#Id
private Date created;
#OneToMany(mappedBy = "", fetch = FetchType.EAGER, cascade = CascadeType.ALL)
private List<Product> products;
public CalculationList(Date created) {
this.created = created;
this.products = new LinkedList<>();
}
public CalculationList(Date created, List<Product> products) {
this.created = created;
this.products = products;
}
public CalculationList() {
}
public Date getCreated() {
return created;
}
public void setCreated(Date created) {
this.created = created;
}
public List<Product> getProducts() {
return products;
}
public void setProducts(List<Product> entities) {
this.products = entities;
}
#Override
public String toString() {
return "CalculationList{" +
"created=" + created +
", products=" + products +
'}';
}
}
CalulatorEntity:
#Entity(name = "calculator_entity")
public class CalculatorEntity implements Serializable {
#Id
private int id;
private CalculatorEntityType type;
private String name;
private int number;
#ManyToOne(cascade = CascadeType.ALL)
private Product product;
public CalculatorEntity(CalculatorEntityType type) {
this.type = type;
}
protected CalculatorEntity() {
}
public int getNumber() {
return number;
}
public void setNumber(int number) {
this.number = number;
}
public Product getProduct() {
return product;
}
public void setProduct(Product product) {
this.product = product;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public CalculatorEntityType getType() {
return type;
}
public void setType(CalculatorEntityType type) {
this.type = type;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public enum CalculatorEntityType {
GAS_PUMP, DELIVERY_BILL;
}
#Override
public String toString() {
return "CalculatorEntity{" +
"id=" + id +
", type=" + type +
", name='" + name + '\'' +
", product=" + product +
", number=" + number +
'}';
}
}
Product:
#Entity(name = "product")
public class Product implements Serializable {
#Id
private int id;
private String name;
private ProductType type;
#OneToMany(mappedBy = "product", fetch = FetchType.EAGER, cascade = CascadeType.ALL)
private List<CalculatorEntity> entities;
public Product(String name, ProductType type) {
this.name = name;
this.type = type;
this.entities = new LinkedList<>();
}
/**
* JPA - Konstruktor
*/
public Product() {
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public ProductType getType() {
return type;
}
public void setType(ProductType type) {
this.type = type;
}
public List<CalculatorEntity> getEntities() {
return entities;
}
public void setEntities(List<CalculatorEntity> entities) {
this.entities = entities;
}
#Override
public String toString() {
return "Product{" +
"id=" + id +
", name='" + name + '\'' +
", type=" + type +
", entities=" + entities +
'}';
}
public enum ProductType {
FUEL("Treibstoff"), OIL("Öl"), OTHER("Verschiedenes");
private String name;
private ProductType(String name) {
this.name = name;
}
public String getName() {
return this.name;
}
}
}
You are using the #OneToMany mapping with the wrong entity, instead of mapping Product class you are mapping CalculationList class, move the following configuration:
#OneToMany(mappedBy = "product", fetch = FetchType.EAGER, cascade = CascadeType.ALL)
private List<CalculatorEntity> entities;
to the Product class.

Parse complex JSON object with GSON with changing content due to OOP

I get such a JSON from the server and want to parse it to my objects:
"product":{
"product_type":"assignment",
"id":717,
"product_profile":{
"title":"new Order from java",
"info":"Some special info",
"dtl_expl":true,
"special_info":""
}
}
Depend on "product_type" value I get different value of "product_profile" from server. The "product_profile" can be one of three types. I created class presentation for each of them. But question is that how organize correct parsing of JSON object to my Product class due to OOP principles? Should I create interface and implement it in each of my three classes, or I should create one parent class and extend it in my three classes to make it work right?
My classes structure. First of all Product class , object of which I should get as a result from json:
public class Product {
ProductAssignment prodAss;
ProductWriting prodWr;
ProductType returnState;
#SerializedName("id")
int id;
#SerializedName("product_type")
String product_type;
#SerializedName("product_profile")
ProductType product_profile;
public Product()
{}
public Product(int id, String product_type, ProductType product_profile)
{
this.id = id;
this.product_type = product_type;
this.product_profile = product_profile.returnObject(product_type);
}
public int getProductId()
{
return this.id;
}
public String getProductType()
{
return this.product_type;
}
public ProductType getProduct()
{
return product_profile.returnObject(product_type);
}
public void setProductId(int id)
{
this.id = id;
}
public void setProductTitle(String product_type)
{
this.product_type = product_type;
}
public void setProduct(ProductType product_profile)
{
this.product_profile = product_profile;
}
#Override
public String toString() {
return "id=" + id + " " + "title=" + product_type
+ " " + "profile=" + product_profile + "}";
}
}
Now parent class ProductType for two subclasses:
public class ProductType extends ProductType{
String product;
static ProductType productType;
static ProductAssignment productAssignment;
static ProductWriting productWriting;
IProductType component;
private ProductType returnState;
ProductAssignment prodAss;
ProductWriting prodWr;
public ProductType()
{
}
public ProductType(IProductType c)
{
component = c;
}
// implemented method of interface
#Override
public ProductType returnObject(String product_type)
{
System.out.println("ProductType");
if (product_type.equals("assignment"))
returnState = prodAss.returnObject(product_type);
else if (product_type.equals("writing"))
returnState = prodWr.returnObject(product_type);
System.out.println(returnState.getClass().getName());
return returnState;
}
}
One of the subclasses:
public class ProductWriting extends ProductType{
#SerializedName("id")
int id;
#SerializedName("title")
String title;
#SerializedName("pages_number")
int pages_number;
#SerializedName("number_of_references")
String number_of_references;
#SerializedName("dtl_expl")
boolean dtl_expl;
#SerializedName("info")
String info;
public ProductWriting()
{}
public ProductWriting(String title, String info, boolean dtl_expl,
int pages_number ,
int id,String number_of_references)
{
this.title = title;
this.info = info ;
this.dtl_expl = dtl_expl;
this.id = id;
this.pages_number = pages_number;
this.number_of_references = number_of_references;
}
public ProductWriting(IProductType c){
super(c);
}
// getters and setters
#Override
public ProductType returnObject(String res) {
System.out.println("Writing");
super.returnObject(res);
return new ProductWriting();
}
}
Another one :
public class ProductAssignment extends ProductType{
ProductAssignment thisObj;
#SerializedName("title")
String title;
#SerializedName("info")
String info;
#SerializedName("dtl_expl")
boolean dtl_expl;
#SerializedName("special_info")
String special_info;
#SerializedName("shoot_exclusive_video")
boolean shoot_exclusive_video;
#SerializedName("shoot_common_video")
boolean shoot_common_video;
public ProductAssignment()
{}
public ProductAssignment(String title, String info, boolean dtl_expl, String special_info,
boolean shoot_common_video, boolean shoot_exclusive_video)
{
this.title = title;
this.info = info ;
this.dtl_expl = dtl_expl;
this.special_info = special_info;
this.shoot_common_video = shoot_common_video;
this.shoot_exclusive_video =shoot_exclusive_video;
}
// getters and setters
#Override
public ProductType returnObject(String res) {
System.out.println("Assignment");
super.returnObject(res);
return new ProductAssignment();
}
#Override
public String toString()
{
return "title=" + title + "info " + "=" + info
+ " " + "profile=" + dtl_expl + "}";
}
}
Interface for binding my classes:
public interface IProductType
{
ProductType returnObject(String parse);
}
I tried to implement in this way, but it doesn't work for now with it.

Categories

Resources