JSON to XML conversion using spring for Multiple models - java

I have a use case in which i get a JSON from a service and i need to create a custom XML out of it.
JSON we receive
[StudentDetails(Name=Lucky, Section=B, Subjects={}, StudentId=4, Hobies=MCCDDVP1, Principal=Mary),
StudentDetails(Name=Brendon, Section=A, Subjects={}, StudentId=1, Hobies=MCCDDVP1, Principal=Mary),
StudentDetails(Name=Gina, Section=A, Subjects={}, StudentId=2, Hobies=MCCDDVP1, Principal=Mary),
StudentDetails(Name=Monica, Section=A, Subjects={}, StudentId=3, Hobies=MCCDDVP1, Principal=Mary),
StudentDetails(Name=Stephen, Section=B, Subjects={}, StudentId=5, Hobies=MCCDDVP1, Principal=Mary),
StudentDetails(Name=Justin, Section=B, Subjects={}, StudentId=6, Hobies=MCCDDVP1, Principal=Mary),
StudentDetails(Name=Sony, Section=B, Subjects={}, StudentId=7, Hobies=MCCDDVP1, Principal=Mary)]
XML to create
<Class>
<Section>A
<Subject>Maths
<Student>Brendon
<Gender>M</Gender>
<RollNumber>1</RollNumber>
</Student>
<Student>Monica
<Gender>F</Gender>
<RollNumber>2</RollNumber>
</Student>
</Subject>
<Subject>English
<Student>Brendon
<Gender>M</Gender>
<RollNumber>1</RollNumber>
</Student>
<Student>Gina
<Gender>F</Gender>
<RollNumber>3</RollNumber>
</Student>
</Subject>
</Section>
<Section>B
<Subject>Science
<Student>Justin
<Gender>M</Gender>
<RollNumber>4</RollNumber>
</Student>
<Student>Sony
<Gender>F</Gender>
<RollNumber>2</RollNumber>
</Student>
</Subject>
<Subject>English
<Student>Stephen
<Gender>M</Gender>
<RollNumber>5</RollNumber>
</Student>
<Student>Lucky
<Gender>F</Gender>
<RollNumber>3</RollNumber>
</Student>
</Subject>
</Section>
<PrincipalName>Mary</PrincipalName>
</Class>
Now i did create the model class for each attribute like below
Class.java
#XmlRootElement
public class Class{
private List<Section> section;
public List<Section> getSection() {
return section;
}
#XmlElement
public void setSection(List<Section> section) {
this.section= section;
}
}
Section.java
public class Section{
private Subject subject;
public Subject getsubject() {
return section;
}
public void setsubject(Subject subject) {
this.subject= subject;
}
}
and so on for all the inner attributes.
Now i am not sure how to create the XML out of these models and get the values from the given JSON as per the XML we need to render using spring boot.
Below is the solution i tried
Created 2 more class just to start with.
Subject.java
public class Subject {
private Student student;
public Student getStudent() {
return student;
}
public void setStudent(Student student) {
this.student = student;
}
public Subject(Student student) {
super();
this.student = student;
}
private Map<String, List<Student>> studentSubjectMap;
public Map<String, List<Student>> getstudentSubjectMap() {
return bPartnerEndPointMap;
}
public void setstudentSubjectMap(Map<String, List<Student>> studentSubjectMap) {
this.studentSubjectMap = studentSubjectMap;
}
}
Student.java
public class Student {
private String gender;
private int rollNumber
//Getter and Setter
//All Args constructor
}
From the json values i set the values in the classes and then tried to marshal the Class.java as below
Main.java
Subject subjectList = new Subject((Student.stream().collect(Collectors.groupingBy
(Student::getSubject,
Collectors.mapping(Student::getRollNumber,Collectors.toList())))));
Section section = new Section(subjectList);
Class class = new Class(section);
JAXBContext jc;
try {
jc = JAXBContext.newInstance(Class.class); // line #1
Marshaller m = jc.createMarshaller();
m.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
m.marshal(combinedXml, file);
m.marshal(combinedXml, System.out);
} catch (JAXBException e) {
log.error("Error while creating XML", e);
}
After doing this i am getting below error at line #1
com.sun.xml.internal.bind.v2.runtime.IllegalAnnotationsException: 1 counts of IllegalAnnotationExceptions
at com.sun.xml.internal.bind.v2.runtime.IllegalAnnotationsException$Builder.check(IllegalAnnotationsException.java:91)
at com.sun.xml.internal.bind.v2.runtime.JAXBContextImpl.getTypeInfoSet(JAXBContextImpl.java:445)
at com.sun.xml.internal.bind.v2.runtime.JAXBContextImpl.<init>(JAXBContextImpl.java:277)
at com.sun.xml.internal.bind.v2.runtime.JAXBContextImpl.<init>(JAXBContextImpl.java:124)
at com.sun.xml.internal.bind.v2.runtime.JAXBContextImpl$JAXBContextBuilder.build(JAXBContextImpl.java:1123)
Below are the ways i tried to set the values in the Pojo
a. Using #Builder
b. Using #Autowiring
c. Using "new" Keyword.
Getting the same error only number of annotations in error changes.
I am not sure what am i missing.

You need to
create a JAXBContext from your model class
Create a Marshaller from your JAXBContext; then
call the marshall method, passing in your model object
An example that will return an XML string is below...
public String getXMLString(Class obj) {
JAXBContext jc = JAXBContext.newInstance(Class.class)
Marshaller m = jc.createMarshaller();
m.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
StringWriter sw = new StringWriter();
m.marshal(obj, sw);
return sw.toString();
}

Related

a nested xml unmarshalling using jaxb

I have an xml structure as follows:
<?xml version="1.0" encoding="UTF-8"?>
<school>
<students>
<student>
<firstName>A</firstName>
<id>1</id>
<lastName>C</lastName>
<company>BCD</company>
<responsibilities>
<responsibility>Leader</responsibility>
<responsibility>Dancer</responsibility>
<responsibility>Reporter</responsibility>
</responsibilities>
</student>
<student>
<firstName>B</firstName>
<id>2</id>
<lastName>C</lastName>
<company>EFG</company>
<responsibilities>
<responsibility>Singer</responsibility>
</responsibilities>
</student>
</students>
<Teachers>
<Teacher>
<firstName>A</firstName>
<lastName>C</lastName>
<responsibilities>
<responsibility>English</responsibility>
<responsibility>Hindi</responsibility>
<responsibility>Softskills</responsibility>
</responsibilities>
</Teacher>
<Teacher>
<firstName>A</firstName>
<lastName>C</lastName>
<company>BCD</company>
<responsibilities>
<responsibility>Science</responsibility>
<responsibility>Math</responsibility>
</responsibilities>
</Teacher>
</Teachers>
</school>
I would want to dynamically parse all the objects and put it into a list.
I have created classes for school,students,student,teacher,teacehrs,responsibilities, responsibility.
These are shown below:
import lombok.Data;
#Data
#XmlRootElement(name="school")
public class School {
private List<Students> Students;
private List<Teachers> Teachers;
}
#Data
public class Students {
private List<Student> student;
}
#Data
public class Student {
private long Id;
private String firstName;
private String lastName;
private String company;
private Responsibilities Responsibilities;
}
#XmlAccessorType(XmlAccessType.PROPERTY)
public class Responsibilities {
public List<String> responsibility ;
}
public class Responsibility {
private String responsibility;
}
#Data
public class Teachers {
private List<Teacher> teacher;
}
public class Teacher {
private String firstName;
private String lastName;
private String company;
private Responsibilities Responsibilities;
}
Also i have the main parsing file, where i want to generically pass all the objects through the root tag (school).
public class ParsingXML {
public static void main(String[] args) {
// TODO Auto-generated method stub
List<School> Entries = new ArrayList<School>();
try {
File xmlFile = new File("Student.xml");
JAXBContext jaxbContext;
jaxbContext = JAXBContext.newInstance(School.class);
Unmarshaller jaxbUnmarshaller = jaxbContext.createUnmarshaller();
School entries = (School) jaxbUnmarshaller.unmarshal(xmlFile);
Entries.add(entries);
/*
* for(Student s: students.getStudent()) { System.out.println(s); }
*/
}
catch (JAXBException e)
{
e.printStackTrace();
}
ListIterator<School> litr = Entries.listIterator();
System.out.println(Entries.size());
//System.out.println("\n Using list iterator");
while(litr.hasNext()){
System.out.println(litr.next());
}
}
}
I expect to get Teachers entries too.. But I get ony students here.
Output:
School(Students=[Students(student=[Student(Id=1, firstName=A, lastName=C, company=BCD, Responsibilities=Responsibilities(responsibility=[Leader, Dancer, Reporter])), Student(Id=2, firstName=B, lastName=C, company=EFG, Responsibilities=Responsibilities(responsibility=[Singer]))])], Teachers=null)
Please suggest me my mistakes and provide me some guidance here
The following code would work for you:
School.class
#XmlRootElement(name = "school")
#Data
#XmlAccessorType(XmlAccessType.FIELD)
public class School {
#XmlElementWrapper(name="students")
#XmlElement(name="student")
private List<Student> students;
#XmlElementWrapper(name="Teachers")
#XmlElement(name="Teacher")
private List<Teacher> responsibilities;
}
Teacher.class:
#Data
#XmlAccessorType(XmlAccessType.NONE)
public class Teacher {
#XmlElement(name="firstName")
private String firstName;
#XmlElement(name="lastName")
private String lastName;
#XmlElementWrapper(name="responsibilities")
#XmlElement(name="responsibility")
private List<String> responsibilities;
}
Student.class:
#Data
#XmlAccessorType(XmlAccessType.NONE)
public class Student {
#XmlElement(name="id")
private long Id;
#XmlElement(name="firstName")
private String firstName;
#XmlElement(name="lastName")
private String lastName;
#XmlElement(name="company")
private String company;
#XmlElementWrapper(name="responsibilities")
#XmlElement(name="responsibility")
private List<String> responsibilities;
}
Main.class:
public class Main {
public static void main(String[] args) throws JAXBException, XMLStreamException {
final InputStream inputStream = Main.class.getClassLoader().getResourceAsStream("students.xml");
final XMLStreamReader xmlStreamReader = XMLInputFactory.newInstance().createXMLStreamReader(inputStream);
final Unmarshaller unmarshaller = JAXBContext.newInstance(School.class).createUnmarshaller();
final School school = unmarshaller.unmarshal(xmlStreamReader, School.class).getValue();
System.out.println(school.toString());
Marshaller marshaller = JAXBContext.newInstance(School.class).createMarshaller();
marshaller.setProperty(Marshaller.JAXB_FRAGMENT, Boolean.TRUE);
marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, Boolean.TRUE);
marshaller.marshal(school, System.out);
}
}
Following is the output:
School(students=[Student(Id=1, firstName=A, lastName=C, company=BCD, responsibilities=[Leader, Dancer, Reporter]), Student(Id=2, firstName=B, lastName=C, company=EFG, responsibilities=[Singer])], responsibilities=[Teacher(firstName=A, lastName=C, responsibilities=[English, Hindi, Softskills]), Teacher(firstName=A, lastName=C, responsibilities=[Science, Math])])
<school>
<students>
<student>
<id>1</id>
<firstName>A</firstName>
<lastName>C</lastName>
<company>BCD</company>
<responsibilities>
<responsibility>Leader</responsibility>
<responsibility>Dancer</responsibility>
<responsibility>Reporter</responsibility>
</responsibilities>
</student>
<student>
<id>2</id>
<firstName>B</firstName>
<lastName>C</lastName>
<company>EFG</company>
<responsibilities>
<responsibility>Singer</responsibility>
</responsibilities>
</student>
</students>
<Teachers>
<Teacher>
<firstName>A</firstName>
<lastName>C</lastName>
<responsibilities>
<responsibility>English</responsibility>
<responsibility>Hindi</responsibility>
<responsibility>Softskills</responsibility>
</responsibilities>
</Teacher>
<Teacher>
<firstName>A</firstName>
<lastName>C</lastName>
<responsibilities>
<responsibility>Science</responsibility>
<responsibility>Math</responsibility>
</responsibilities>
</Teacher>
</Teachers>
</school>

Unmarshalling with complex type

I'm having trouble unmarshalling XML that represents an invoice in this form:
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<soap:Body>
<Response xmlns="Lamp">
<Result>
<Title>The Title</Title>
<Lines>
<anyType xsi:type="InvoiceLine">
<Name>Name One</Name>
<Quantity>1.0000</Quantity>
</anyType>
<anyType xsi:type="InvoiceLine">
<Name>Name Two</Name>
<Quantity>2.0000</Quantity>
</anyType>
</Lines>
</Result>
</Response>
</soap:Body>
</soap:Envelope>
The Invoice class:
#XmlAccessorType(XmlAccessType.FIELD)
public class Invoice {
public String Title;
public List<InvoiceLine> Lines;
}
The InvoiceLine class:
#XmlAccessorType(XmlAccessType.FIELD)
public class InvoiceLine extends anyType {
public String Name;
public float quantity;
}
And a abstract class for anyType:
public abstract class anyType {}
This is the code I'm using to do the unmarshalling:
public static void main(String[] args) throws Exception {
InputStream is = new FileInputStream("input.xml");
XMLInputFactory xif = XMLInputFactory.newFactory();
XMLStreamReader xsr = xif.createXMLStreamReader(is);
xsr.nextTag();
while(!xsr.getLocalName().equals("Result")) {
xsr.nextTag();
}
JAXBContext jc = JAXBContext.newInstance(Invoice.class, InvoiceLine.class);
javax.xml.bind.Unmarshaller unmarshaller = jc.createUnmarshaller();
JAXBElement<Invoice> jb = unmarshaller.unmarshal(xsr, Invoice.class);
xsr.close();
System.out.println(jb.getValue());
}
The problem is the Lines list only contains 1 entry with Name=null and quantity=0.0.
-- Edit:
I've just tried adding XmlElement and XmlElementWrapper annotations to both classes, the only change is that the Lines list has no elements.
Modified Invoice:
#XmlAccessorType(XmlAccessType.FIELD)
public class Invoice {
#XmlElement(name = "Title")
public String Title;
#XmlElement(name = "anyType")
#XmlElementWrapper(name = "Lines")
public List<InvoiceLine> Lines;
}
Modified InvoiceLine:
#XmlAccessorType(XmlAccessType.FIELD)
public class InvoiceLine extends anyType {
#XmlElement(name = "Name")
public String Name;
#XmlElement(name = "Quantity")
public float Quantity;
}
-- Edit:
I've just tried making the attributes lower case, but still no luck.

How can I get the attribute value?(javax.xml.bind.annotation)

Im using javax.xml.bind.annotation and I need to get the attribute "xmlns:language" (see below xml)
<type xmlns:language="ru" xmlns:type="string">Some text</type>
What annotation should I use?
#XmlRootElement(name = "type")
#XmlAccessorType(XmlAccessType.FIELD)
public class Type {
#XmlValue
protected String value;
#XmlAttribute
protected String language;
}
In the document from your question you are declaring that the prefix language will be associated with the namespace ru.
<type xmlns:language="ru" xmlns:type="string">Some text</type>
I do not believe the above is what you are trying to do. If you are trying to specify the language for your document I would recommend using the xml:lang attribute instead (as suggested by Ian Roberts).
<type xml:lang="ru">Some text</type>
Type
Then you would map to it as follows using the #XmlAttribute annotation:
import javax.xml.bind.annotation.*;
#XmlRootElement
#XmlAccessorType(XmlAccessType.FIELD)
public class Type {
#XmlValue
protected String value;
#XmlAttribute(name = "lang", namespace = "http://www.w3.org/XML/1998/namespace")
protected String language;
}
Demo
import java.io.StringReader;
import javax.xml.bind.*;
public class Demo {
public static void main(String[] args) throws Exception {
JAXBContext jc = JAXBContext.newInstance(Type.class);
Unmarshaller unmarshaller = jc.createUnmarshaller();
StringReader xml = new StringReader(
"<type xml:lang='ru' xmlns:type='string'>Some text</type>");
Type type = (Type) unmarshaller.unmarshal(xml);
Marshaller marshaller = jc.createMarshaller();
marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
marshaller.marshal(type, System.out);
}
}
Output
<?xml version="1.0" encoding="UTF-8"?>
<type xml:lang="ru">Some text</type>

Java Code to XML/XSD without using Annotation

I need to marshall and unmarshall a Java class to XML. The class in not owned by me, that I cannot add anotations so that I can use JAXB.
Is there a good way to convert the Java to XML with the given contraint?
Also, thought a tool may be helpful, but I would be more intersted it there is some Java API to do the same.
Note: I'm the EclipseLink JAXB (MOXy) lead and a member of the JAXB (JSR-222) expert group.
DOMAIN MODEL
I will use the following domain model for this answer. Note how there are no JAXB annotations on the model.
Customer
package forum11693552;
import java.util.*;
public class Customer {
private String firstName;
private String lastName;
private List<PhoneNumber> phoneNumbers = new ArrayList<PhoneNumber>();
public String getFirstName() {
return firstName;
}
public void setFirstName(String firstName) {
this.firstName = firstName;
}
public String getLastName() {
return lastName;
}
public void setLastName(String lastName) {
this.lastName = lastName;
}
public List<PhoneNumber> getPhoneNumbers() {
return phoneNumbers;
}
public void setPhoneNumbers(List<PhoneNumber> phoneNumbers) {
this.phoneNumbers = phoneNumbers;
}
}
PhoneNumber
package forum11693552;
public class PhoneNumber {
private String type;
private String number;
public String getType() {
return type;
}
public void setType(String type) {
this.type = type;
}
public String getNumber() {
return number;
}
public void setNumber(String number) {
this.number = number;
}
}
OPTION #1 - Any JAXB (JSR-222) Implementation
JAXB is configurartion by exception, this means you only need to add annotations where you want the mapping behaviour to differ from the default. Below is a link to an example demonstrating how to use any JAXB impl without annotations:
Demo
package forum11693552;
import javax.xml.bind.*;
import javax.xml.namespace.QName;
public class Demo {
public static void main(String[] args) throws Exception {
JAXBContext jc = JAXBContext.newInstance(Customer.class);
Customer customer = new Customer();
customer.setFirstName("Jane");
customer.setLastName("Doe");
PhoneNumber workPhone = new PhoneNumber();
workPhone.setType("work");
workPhone.setNumber("555-1111");
customer.getPhoneNumbers().add(workPhone);
Marshaller marshaller = jc.createMarshaller();
marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
JAXBElement<Customer> rootElement = new JAXBElement<Customer>(new QName("customer"), Customer.class, customer);
marshaller.marshal(rootElement, System.out);
}
}
Output
<customer>
<firstName>Jane</firstName>
<lastName>Doe</lastName>
<phoneNumbers>
<number>555-1111</number>
<type>work</type>
</phoneNumbers>
</customer>
For More Information
http://wiki.eclipse.org/EclipseLink/Examples/MOXy/GettingStarted/TheBasics
OPTION #2 - EclipseLink JAXB (MOXy)'s External Mapping Document
If you do want to customize the mappings, then you may be interested in MOXy's external mapping document extension. A sample mapping document looks like the following:
oxm.xml
<?xml version="1.0"?>
<xml-bindings xmlns="http://www.eclipse.org/eclipselink/xsds/persistence/oxm"
package-name="forum11693552">
<java-types>
<java-type name="Customer">
<xml-root-element />
<java-attributes>
<xml-element java-attribute="firstName" name="first-name" />
<xml-element java-attribute="lastName" name="last-name" />
<xml-element java-attribute="phoneNumbers" name="phone-number" />
</java-attributes>
</java-type>
<java-type name="PhoneNumber">
<java-attributes>
<xml-attribute java-attribute="type" />
<xml-value java-attribute="number" />
</java-attributes>
</java-type>
</java-types>
</xml-bindings>
jaxb.properties
To enable MOXy as your JAXB provider you need to include a file called jaxb.properties in the same package as your domain model with the following entry (see: http://blog.bdoughan.com/2011/05/specifying-eclipselink-moxy-as-your.html):
javax.xml.bind.context.factory=org.eclipse.persistence.jaxb.JAXBContextFactory
Demo
When using EclipseLink MOXy as your JAXB provider (see), you can leverage the external mapping document when you bootstrap your JAXBContext
package forum11693552;
import java.util.*;
import javax.xml.bind.*;
import javax.xml.namespace.QName;
import org.eclipse.persistence.jaxb.JAXBContextFactory;
public class Demo {
public static void main(String[] args) throws Exception {
Map<String, Object> properties = new HashMap<String,Object>(1);
properties.put(JAXBContextFactory.ECLIPSELINK_OXM_XML_KEY, "forum11693552/oxm.xml");
JAXBContext jc = JAXBContext.newInstance(new Class[] {Customer.class}, properties);
Customer customer = new Customer();
customer.setFirstName("Jane");
customer.setLastName("Doe");
PhoneNumber workPhone = new PhoneNumber();
workPhone.setType("work");
workPhone.setNumber("555-1111");
customer.getPhoneNumbers().add(workPhone);
Marshaller marshaller = jc.createMarshaller();
marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
JAXBElement<Customer> rootElement = new JAXBElement<Customer>(new QName("customer"), Customer.class, customer);
marshaller.marshal(rootElement, System.out);
}
}
Output
<?xml version="1.0" encoding="UTF-8"?>
<customer>
<first-name>Jane</first-name>
<last-name>Doe</last-name>
<phone-number type="work">555-1111</phone-number>
</customer>
For More Information
http://blog.bdoughan.com/2010/12/extending-jaxb-representing-annotations.html
http://blog.bdoughan.com/2012/04/extending-jaxb-representing-metadata-as.html
Have you looked at XStream ? It will deserialise/deserialise a standard POJO without annotations or XSDs. You can provide customisations to affect how elements appear in the XML and pretty much works out-of-the-box.
You could write a custom XmlAdapter and annotate fields of the constrained type with a XmlJavaTypeAdapter annotation. The basics would be something like this:
public enum CannotBeAnnotated { value1, value2; }
#XmlRootElement(name="client")
public class ClientClass {
#XmlJavaTypeAdapter(Bridge.class)
public CannotBeAnnotated;
}
#XmlRootElement(name="representation")
public class XmlType {
#XmlValue
public String value;
}
public class Bridge extends XmlAdapter<XmlType, CannotBeAnnotated>{
public XmlType marshal(CannotBeAnnotated c) {
XmlType x=new XmlType();
x.value=c.name();
return x;
}
public CannotBeAnnotated unmarshall(XmlType x) {
return CannotBeAnnotated.valueOf(x.value);
}
}
Of course for enums this would not be useful as JAXB knows how to deal with them. I just picked an enum for simplicity so you can see the idea:
Design an XML representation that you do control
Write an adapter converting that Java type into the desired type
Annotate "client" code referencing the adapter for the desired type
Profit.

Parse xml based on attributes using JAXB

I am using JAXB to parse some xml.
<countries>
<Name language="en">Australia</Name>
<Name language="se">Australien</Name>
</countries>
If I in my class Countries use
#XmlElement(name = "Name", required = true)
protected List<Name> name;
everything works.
However I would like to only get the attribute where language="en"
So I in my Countries class have
protected String name
not a collection.
Is there a good way to solve this with some annotation for example?
Note: I'm the EclipseLink JAXB (MOXy) lead and a member of the JAXB (JSR-222) expert group
Below are two ways you could handle this use case. The first is a little more code but could be done with any JAXB implementation. The second is less code, but requires you use EclipseLink JAXB (MOXy).
OPTION #1 - ANY JAXB (JSR-222) IMPLEMENTATION
Demo
You could use a filtered stream reader to filter out the unwanted elements and have your JAXB implementation unmarshal that.
package forum11586106;
import javax.xml.bind.*;
import javax.xml.stream.*;
import javax.xml.transform.stream.StreamSource;
public class Demo {
private static final String LANGUAGE_CODE = "en";
public static void main(String[] args) throws Exception {
XMLInputFactory xif = XMLInputFactory.newFactory();
XMLStreamReader xsr = xif.createXMLStreamReader(new StreamSource("src/forum11586106/input.xml"));
xsr = xif.createFilteredReader(xsr, new StreamFilter() {
private boolean isReading = true;
#Override
public boolean accept(XMLStreamReader reader) {
if(reader.isStartElement() && "Name".equals(reader.getLocalName())) {
isReading = LANGUAGE_CODE.equals(reader.getAttributeValue("", "language"));
return isReading;
} else if(reader.isEndElement() && !isReading) {
isReading = true;
return false;
}
return true;
}});
JAXBContext jc = JAXBContext.newInstance(Countries.class);
Unmarshaller unmarshaller = jc.createUnmarshaller();
Countries countries = (Countries) unmarshaller.unmarshal(xsr);
Marshaller marshaller = jc.createMarshaller();
marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
marshaller.marshal(countries, System.out);
}
}
Countries
package forum11586106;
import javax.xml.bind.annotation.*;
#XmlRootElement
public class Countries {
private String name;
#XmlElement(name="Name")
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
input.xml
With this approach the language attribute is not included in the output:
<countries>
<Name language="en">Australia</Name>
<Name language="se">Australien</Name>
</countries>
Output
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<countries>
<Name>Australia</Name>
</countries>
OPTION #2 - ECLIPSELINK JAXB (MOXy)
We will leverage MOXy's #XmlPath extension to map to the Name element that has a language attribute with value en (see http://blog.bdoughan.com/2011/03/map-to-element-based-on-attribute-value.html).
Countries
package forum11586106;
import javax.xml.bind.annotation.*;
import org.eclipse.persistence.oxm.annotations.XmlPath;
#XmlRootElement
public class Countries {
private String name;
#XmlPath("Name[#language='en']/text()")
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
jaxb.properties
To use MOXy as your JAXB provider you need to include a file called jaxb.properties in the same package as your domain model with the following entry (see: http://blog.bdoughan.com/2011/05/specifying-eclipselink-moxy-as-your.html).
javax.xml.bind.context.factory=org.eclipse.persistence.jaxb.JAXBContextFactory
Demo
With this approach the element filtering is handled by the #XmlPath mapping, so the runtime portion becomes much simpler. Note how only the standard JAXB runtime APIs are used.
package forum11586106;
import java.io.File;
import javax.xml.bind.*;
public class Demo {
public static void main(String[] args) throws Exception {
JAXBContext jc = JAXBContext.newInstance(Countries.class);
Unmarshaller unmarshaller = jc.createUnmarshaller();
File xml = new File("src/forum11586106/input.xml");
Countries countries = (Countries) unmarshaller.unmarshal(xml);
Marshaller marshaller = jc.createMarshaller();
marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
marshaller.marshal(countries, System.out);
}
}
input.xml
<countries>
<Name language="en">Australia</Name>
<Name language="se">Australien</Name>
</countries>
Output
With this approach the language attribute is included in the output:
<?xml version="1.0" encoding="UTF-8"?>
<countries>
<Name language="en">Australia</Name>
</countries>

Categories