How to unmarshal array or list using JAXB? - java

I have an XML file and I need to set it up with my POJO class
<ids xmlns:a="http://schemas.microsoft.com/2003/10/Serialization/Arrays" >
<a:string>100</a:string>
<a:string>101</a:string>
<a:string>102</a:string>
... etc..
</ids>
Which annotation do I have to use, to fetch these values
I am using the following way.
#XmlElement(name="string",namespace="http://schemas.microsoft.com/2003/10/Serialization/Arrays")
protected List<String> id;
but I am getting null

You did not present the class containing protected List<String> id. It should be something like
#XmlRootElement(name = "ids")
public class Wrapper {
#XmlElement(name = "string",
namespace = "http://schemas.microsoft.com/2003/10/Serialization/Arrays")
protected List<String> id;
}
to have the list populated. Also you can name the class Ids and remove name = 'Ids'.

Related

Single Tag XML to multiple fields JAXB

I have the following problem, with JAXB library.
XML
<par>
<name>content</name>
<value>This is a value</value>
</par>
<par>
<name>map</name>
<value>
<info>
<obj>obj1</obj>
</info>
</value>
</par>
Class Java
public static class Par {
#XmlElement(name = "name")
private String name;
#XmlElement(name = "value")
private Info valueObject;
#XmlAnyElement(lax = true)
private Node valueString;
}
public static class Info {
#XmlElement(name = "obj")
private String obj;
}
the problem is that the Info field is mapped correctly, so in the value valueObject i have the full object Info. But the string This is a value is not mapped, because JAXB library maps only the first field called "value".
What can I do to map the value string?
I found on StackOverflow one similar question, but in the response they said to use the object Node (DOM library). So, the field is treated as a String, and in a second moment they used another time the JAXB library to map the complex Object.
So, this is not a good solution for my script because I have a list of string and a list of object; then, if the list is very long I will have to read the list.
What can I do ? Can you help me ?
Thanks a lot

How can I create a one-to-many relationship with Spring and thymeleaf?

I would like to create a "Product" class which has multiple "Tags" as a collection. So a one-to-many database whereas Product is "one" and Tags is "many".
The Tags will be defined in the HTML as an Input field and divided by spaces. For example "tag1 tag2 tag3".
My question now is: How can I retrieve the string from the input field and append them as a Collection to my product object?
What I have so far:
Product
#Entity
public class Product {
#Id
#GeneratedValue
private int barcode;
public String name;
#OneToMany(mappedBy = "product", cascade = CascadeType.ALL)
private Collection<Tag> tags;
...Getter & Setter
Tag
#Entity
public class Tag {
#Id
private String tagname;
#ManyToOne(fetch = FetchType.LAZY, optional = false)
#JoinColumn(name = "barcode", nullable = false)
private Product product;
...Getter & Setter
ProductsController: I tried to add Tag objects as a test but that throws errors that the Tag table does not exist
#PostMapping("/add")
public String add(#Valid Product product, BindingResult result, Model model) {
model.addAttribute("responseMessage", result);
if(!result.hasErrors()) {
//I tried to add a static collection to the product object, but it throws errors
Collection<Tag> col = new ArrayList<>();
col.add(new Tag("test"));
col.add(new Tag("test2"));
product.setTags(col);
productRepository.save(product);
}
model.addAttribute("products",productRepository.findAll());
return "products-add";
}
As you have the tags separated by space. First of all you need to make a String Array of tags using regex as shown below.
String tags = "tag1 tag2 tag3";
String[] tagArr = tags.split("\\s+");
Now you need to create a repository as below.
#Repository
public interface TagRepository extends JpaRepository<Tag, Long> {
Tag findByTagname(String tagname);
}
Create an interface for TagService.
public interface TagService {
Tag findByTagname(String tagname);
}
Create an implementation of TagService class
#Service
public class TagServiceImpl implements TagService{
#Autowired
private TagRepository tagRepository;
#Override
public Tag findByTagname(String tagname) {
return tagRepository.findByTagname(tagname);
}
}
Now fetching Tag by name is completed. Autowire your TagService into your controller class
#Autowire
private TagService tagService;
Add the below code to your controller.
String tags = "tag1 tag2 tag3";
String[] tagArr = tags.split("\\s+");
List<Tag> tagList = new ArrayList<Tag>();
for (String tagname : tagArr) {
Tag tag = tagService.findbyTagname(tagname);
tagList.add(tag);
}
Now when you save your product class. Set this list of tag into your it.
You must have a ProductService class in your service layer annotated with #Service.
You must create a method annotated with #Transactional in ProductService class
Your method must read all tags of a product and transform it to any collection you want.
Don't do it inside Controller. It is actually bad place to do same things.
Probably you are running this for first time and your database does not have table named Tag. Please make sure hibernate property hibernate.hbm2ddl.auto is set to 'update' which will automatically create required tables first before doing any insert of data.
And for converting string value tags separated by space, just use string.split() method which gives you array and then convert it to List to be set in Product object.

JAXB - Controlling element names for lists inside wrapper class

I have a class wrapping up a list and some other attribute and in another class I have multiple instances of the wrapper type. I know that we can control the name of elements by adding XmlElement annotation to whichever element we need. Is there any way to specify the element name corresponding to the contents of a wrapper type from wherever the wrapper type is used?
For instance, the wrapper class looks like
#XmlAccessorType(XmlAccessType.FIELD)
public class Wrapper {
#XmlElement(name = "name")
private List<String> names;
private String comment;
//Getters and setters
}
And the wrapper is used as
private Wrapper employeeNames;
private Wrapper departmentNames;
private Wrapper someOtherNames;
Can I in someway annotate these fields, so that I would have XML formed as
<employeeNames>
<employeeName>ABC DEF</employeeName>
<employeeName>PQR STU</employeeName>
<employeeName>ABC</employeeName>
</employeeNames>
<departmentNames>
<departmentName>PQR</departmentName>
<departmentName>PQR STU</departmentName>
<departmentName>MNO</departmentName>
</departmentNames>
I know that if don't have the wrapper and directly use the lists, I can have #XmlElementWrapper and #XmlElement annotations used to build the XML like this.
You could try this:
#XmlAccessorType(XmlAccessType.FIELD)
public class Wrapper {
#XmlElements({
#XmlElement(name = "employeeName", type = EmployeeName.class),
#XmlElement(name = "departmentName", type = DepartmentName.class),
})
private List<Name> names;
private String comment;
public void setNames(List<Name> names) {
this.names = names;
}
}
and then EmployeeName and DepartmentName something like this:
#XmlAccessorType(XmlAccessType.FIELD)
public class EmployeeName implements Name {
#XmlValue
private String name;
public EmployeeName(String name) {
this.name = name;
}
}
They both implement an interface that doesn't do much:
public interface Name { }
I changed your List<String> to List<Name> in your wrapper, because the marshaling would have problem to differentiate between employeeName and departmentName if they both were of type String.
The only thing that comes to mind is that instead of #XmlElement you could use #XmlElementRef like
#XmlElementRef(name = "name")
private List<JAXBElement<String>> names;
Then you could specify the desired element name in each of the elements of names:
wrapper.names.add(new QName("departmentName"), String.class, "PQR");

Json - replace field name in generic Dto with associated type name

I have DTO class with Generics for data transfer
public class CreateDto<E> {
private String id;
private String name;
private E e;
}
My problem is while converting object to json using ObjectMapper, I am always getting json string as
{id : 1, name : 2, e : {"state1":"value1" ...} };
I want that e to be replaced with specific thing like, if it CreateDto<Foo> I want it like a foo:{"state1":"value1" ..}, when it is CreateDto it should be bar : {"state2":"value2"..}
I would like to know whether is there a way to get this using annotation or some other util.
Thought, this is not the same way as you required, the same principal is to use #JsonTypeInfo annotation at the property or method (getter or setter) level.
The following example (at property level);
public class CreateDto<E> {
private String id;
private String name;
#JsonTypeInfo(use = JsonTypeInfo.Id.NAME, include = As.PROPERTY, property = "type")
private E e;
...
}
will produce json string (e.g Foo for E)
{"id":"100","name":"john","e":{"type":"Foo","state1":"value1"...}}
Now it is distinct on the underlying type in generic data transfer object.

Using JAXB annotations, how do you get each XML element of a list wrapped in an element, when each list element has an unknown name?

How is this XML structure modeled in a JAXB class annotation?
<root>
<linksCollection>
<links>
<foo href="http://example.com/foo" rel="link"/>
</links>
<links>
<bar href="http://example.com/bar" rel="link"/>
</links>
</linksCollection>
</root>
Starting with the following root class, what is the Link class? How do you get each link with an unknown element name to be wrapped in the links element?
#XmlRootElement
#XmlAccessorType(XmlAccessType.FIELD)
public class Root {
#XmlElement
protected List<Link> linksCollection;
// etc.
}
The following attempt does not work:
#XmlRootElement(name = "links")
#XmlAccessorType(XmlAccessType.FIELD)
public class Link {
#XmlAnyElement
protected Object link;
#XmlAttribute
protected String href;
#XmlAttribute
protected String rel;
//etc.
}
Your attempt with #XmlAnyElement for the unknown elements is the right way, but you were missing the #XmlElementWrapper for the collection. The following mapping produces a collection for those elements:
#XmlRootElement
#XmlAccessorType(XmlAccessType.FIELD)
public class Root {
#XmlElementWrapper(name="linksCollection")
#XmlElement(name="links")
protected List<Link> linksCollection;
}
public class Link {
#XmlAnyElement(lax = true)
protected Object content;
}
According to this explanation you will have an instance of org.w3c.dom.Element in your collection if you do not specify a mapping.
If you have only a limited subset of unknown elements, you could change the annotation in the link class as follows:
#XmlElements({
#XmlElement(name = "foo", type = FooBar.class),
#XmlElement(name = "bar", type = FooBar.class) , ...})
protected Object content;
The FooBar class could then look like this:
public class FooBar {
#XmlAttribute(name = "href")
protected String href;
#XmlAttribute(name = "rel")
protected String rel;
}
However when you can't predict the possible tags, I would stay with the #XmlAnyElement and add a #XmlTypeAdapter. There is another thread: Jaxb complex xml unmarshall about this topic.

Categories