I'm using JAXB for creating xml. I want to set attribute 'lang' on elements PrimaryValue and AlternativeSpelling.
<AgencyOrUnit>
<PrimaryValue lang="el">ΓΑΔΑ</PrimaryValue>
<AlternativeSpelling lang="en">Athens General Police Directorate</AlternativeSpelling>
</AgencyOrUnit>
Here's my code:
#XmlRootElement(name = "OwnerReference")
#XmlType(propOrder = { "primaryValue", "alternativeSpelling"})
public class AgencyOrUnit {
private String PrimaryValue;
private String AlternativeSpelling;
public String getPrimaryValue() {
return PrimaryValue;
}
public void setPrimaryValue(String PrimaryValue){
this.PrimaryValue = PrimaryValue;
}
public String getAlternativeSpelling() {
return AlternativeSpelling;
}
public void setAlternativeSpelling(String AlternativeSpelling){
this.AlternativeSpelling = AlternativeSpelling;
}
}
Here's process of marshalling:
AgencyOrUnit agencyOrUnit = new AgencyOrUnit();
agencyOrUnit.setPrimaryValue("ΓΑΔΑ");
agencyOrUnit.setAlternativeSpelling("General Police");
The problem is that I don't know how to set property with value on elements primaryValue and alternativeSpelling?
You can use annotations #XmlValue & #XmlAttribute but you need to create a new class to hold both lang and the original value string. Something like this:
#Setter
#AllArgsConstructor
public class LocaleString {
private String lang;
private String value;
#XmlAttribute
public String getLang() {
return lang;
}
#XmlValue
public String getValue() {
return value;
}
}
Then modify your AgencyOrUnit accordingly:
#XmlRootElement(name = "OwnerReference")
#XmlType(propOrder = { "primaryValue", "alternativeSpelling"})
#Getter #Setter
public class AgencyOrUnit {
private LocaleString PrimaryValue;
private LocaleString AlternativeSpelling;
}
Test it:
#Test
void test() throws JAXBException {
AgencyOrUnit agencyOrUnit = new AgencyOrUnit();
agencyOrUnit.setPrimaryValue(new LocaleString("el", "ΓΑΔΑ"));
agencyOrUnit.setAlternativeSpelling(new LocaleString("en", "General Police"));
JAXBContext ctx = JAXBContext.newInstance(AgencyOrUnit.class);
Marshaller marshaller = ctx.createMarshaller();
marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, Boolean.TRUE);
marshaller.marshal(agencyOrUnit, System.out);
}
and you should see this:
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<OwnerReference>
<primaryValue lang="el">ΓΑΔΑ</primaryValue>
<alternativeSpelling lang="en">General Police</alternativeSpelling>
</OwnerReference>
Related
How do I annotate the externalValue and companyId fields in the Root class so that "abc" gets mapped to externalValue and "123" gets mapped to companyId?
Do I need the #XmlJavaTypeAdapter annotation? Where? I'm hoping that if I do, it can just handle those 2 fields and I can leave the annotations for title and countryCodes as-is.
XML:
<item>
<externalValue companyId="123">abc</externalValue>
<title>My Title</title>
<country>US</country>
<country>CA</country>
</item>
POJO:
#XmlRootElement
#XmlAccessorType(XmlAccessType.FIELD)
public class Item {
private String externalValue;
private String companyId;
#XmlElement
private String title;
#XmlElement(name = "country")
public List<String> countryCodes;
// getters and setters...
}
I am afraid that this is not possible to achieve only with annotations (so without extra POJO and some adapter) in general case namely JAXB specs. However if your happen to use MOXy as your JAXB implementation it is easy as adding annotation #XmlPath like this:
#XmlPath("externalValue/#companyId")
private String companyId;
Related question: Unmarshalling an XML using Xpath expression and jaxb
You have to define the class in the following manner.
#XmlRootElement
#XmlAccessorType(XmlAccessType.FIELD)
public class Item {
private CompanyIdValue companyIdValue;
#XmlElement
private String title;
#XmlElement(name = "country")
public List<String> countryCodes;
//getter and setter
}
In case of both attribute in an XML element tag, you have to define a separate class. Define a separate class called CompanyIdValue, for XML element, you have to define #XmlValue and for attribute you have to annotate with #XmlAttribute
import javax.xml.bind.annotation.XmlAttribute;
import javax.xml.bind.annotation.XmlValue;
public class CompanyIdValue {
#XmlElement(name = "externalValue")
private String externalValue;
private String companyId;
public String getExternalValue() {
return externalValue;
}
#XmlValue
public void setExternalValue(String externalValue) {
this.externalValue = externalValue;
}
public String getCompanyId() {
return companyId;
}
#XmlAttribute
public void setCompanyId(String companyId) {
this.companyId = companyId;
}
}
I provide below a test program also for testing.
public class Test {
public static void main(String[] args) {
try {
Item item = new Item();
CompanyIdValue companyIdValue = new CompanyIdValue();
companyIdValue.setCompanyId("SomeId");
companyIdValue.setExternalValue("Some External value");
item.setCompanyIdValue(companyIdValue);
item.setCountryCodes(Arrays.asList("A", "B"));
item.setTitle("Some Title");
JAXBContext jaxbContext = JAXBContext.newInstance(Item.class);
Marshaller jaxbMarshaller = jaxbContext.createMarshaller();
jaxbMarshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, Boolean.TRUE);
jaxbMarshaller.marshal(item, System.out);
} catch (JAXBException e) {
e.printStackTrace();
}
}
}
I've spent a long time trying to figure out how to validate a XML file, and I've got it working with a pre-generated XSD schema. However, I wish to dynamically generate a schema without creating a file, based on the annotation class I have specified, I've tried to not specify any parameters to SchemaFactory, but then it seems to just create an empty schema (see comments below).
Here are the two classes I use for my JAXB reading and writing of a XML file.
This is the code for the class XMLTranslationWrapper:
#XmlAccessorType(XmlAccessType.FIELD)
#XmlRootElement(namespace = "my.package.namespace")
public class XMLTranslationWrapper {
#XmlElementWrapper(name = "TRANSLATIONS")
#XmlElement(name = "TRANSL")
public ArrayList<XMLTranslationNode> translations;
public XMLTranslationWrapper(){
translations = new ArrayList<XMLTranslationNode>();
}
public void setTranslations(ArrayList<XMLTranslationNode> translations){
this.translations = translations;
}
public XMLTranslationNode getTranslation(String code){
for(XMLTranslationNode transl : translations){
if(transl.getCode().equals(code))
return transl;
}
return null;
}
public void addTranslation(XMLTranslationNode translation){
this.translations.add(translation);
}
}
This is the code for the class XMLTranslationNode:
#XmlAccessorType(XmlAccessType.PROPERTY)
#XmlRootElement(name = "TRANSL")
#XmlType(propOrder = { "severity", "translation" })
public class XMLTranslationNode {
private String severity;
private String code;
private String translation;
#XmlElement(name="SEVERITY")
public String getSeverity(){
return this.severity;
}
public void setSeverity(String severity){
this.severity = severity;
}
#XmlAttribute(name="CODE")
public String getCode(){
return this.code;
}
public void setCode(String code){
this.code = code;
}
#XmlElement(name="TRANSLATION")
public String getTranslation(){
return this.translation;
}
public void setTranslation(String translation){
this.translation = translation;
}
}
This is the code I used to generate the pre-generated XSD schema:
public class generateSchema {
public static void main(String[] args) {
JAXBContext jaxbContext;
try {
jaxbContext = JAXBContext.newInstance(XMLTranslationWrapper.class);
SchemaOutputResolver sor = new MySchemaOutputResolver();
jaxbContext.generateSchema(sor);
} catch (JAXBException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
static class MySchemaOutputResolver extends SchemaOutputResolver {
public Result createOutput(String namespaceURI, String suggestedFileName) throws IOException {
File file = new File(suggestedFileName);
StreamResult result = new StreamResult(file);
System.out.println(file.toURI().toURL().toString());
result.setSystemId(file.toURI().toURL().toString());
return result;
}
}
}
Finally here is a sample XML which can be used to test the code:
<?xml version="1.0" encoding="US-ASCII" standalone="yes"?>
<ns2:xmlTranslationWrapper xmlns:ns2="my.package.namespace">
<TRANSLATIONS>
<TRANSL CODE="123">
<SEVERITY>Information</SEVERITY>
<TRANSLATION>ABC</TRANSLATION>
</TRANSL>
</TRANSLATIONS>
</ns2:xmlTranslationWrapper>
How can I dynamically generate my XSD schema without creating a file equivalent to using a pre-generated XSD schema?
As promised. The idea is simple:
First generate your schema into a DOM result
The parse it from the generated DOM
And, finally, use for validation
I don't think you can do it much better than that. JAXB's schemagen internal structures do not seem to be compatible with javax.xml.validation.Schema. So creating DOM and then parsing it back is the easiest way.
Code example:
public class DynamicSchemaTest {
#XmlRootElement
public static class A {
#XmlAttribute(required = true)
public String name;
public A() {
}
public A(String name) {
this.name = name;
}
}
#Test(expected = MarshalException.class)
public void generatesAndUsesSchema() throws JAXBException, IOException,
SAXException {
final JAXBContext context = JAXBContext.newInstance(A.class);
final DOMResult result = new DOMResult();
result.setSystemId("schema.xsd");
context.generateSchema(new SchemaOutputResolver() {
#Override
public Result createOutput(String namespaceUri,
String suggestedFileName) {
return result;
}
});
#SuppressWarnings("deprecation")
final SchemaFactory schemaFactory = SchemaFactory
.newInstance(WellKnownNamespace.XML_SCHEMA);
final Schema schema = schemaFactory.newSchema(new DOMSource(result
.getNode()));
final Marshaller marshaller = context.createMarshaller();
marshaller.setSchema(schema);
// Works
marshaller.marshal(new A("works"), System.out);
// Fails
marshaller.marshal(new A(null), System.out);
}
}
Lets suppose that we have XML consnensual with Schema and Java class with some common fields:
<objectFromSchema1>
<element1/>
<commonElement1/>
<commonElement2/>
<element2/>
</objectFromSchema1>
public class X {
private String element1;
private String commonElement1;
private String commonElement2;
private String element2;
}
Is a nice way to unmarschall such kind of XML to Java object ? It means: convert all consensual fields and set null on rest.
The answer is "yes". This is the way JAXB works. Take a look on basic JAXB tutorial, e.g. https://jaxb.java.net/tutorial/
http://docs.oracle.com/javase/tutorial/jaxb/intro/
http://www.vogella.com/tutorials/JAXB/article.html
"YES"
If you have an xsd you also could generate automatically these classes by <artifactId>maven-jaxb2-plugin</artifactId>maven plugin.
An example of your Class
import java.io.Serializable;
import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlRootElement;
import javax.xml.bind.annotation.XmlType;
#XmlAccessorType(XmlAccessType.FIELD)
#XmlType(name = "objectFromSchema1", propOrder = {
})
#XmlRootElement(name = "objectFromSchema1")
public class ObjectFromSchema1
implements Serializable
{
private final static long serialVersionUID = 12343L;
protected String element1;
protected String element2;
protected String commonElement1;
protected String commonElement2;
public String getElement1() {
return element1;
}
public void setElement1(String element1) {
this.element1 = element1;
}
public String getElement2() {
return element2;
}
public void setElement2(String element2) {
this.element2 = element2;
}
public String getCommonElement1() {
return commonElement1;
}
public void setCommonElement1(String commonElement1) {
this.commonElement1 = commonElement1;
}
public String getCommonElement2() {
return commonElement2;
}
public void setCommonElement2(String commonElement2) {
this.commonElement2 = commonElement2;
}
}
Main method to use it
public static void main(String[] args) throws JAXBException {
final JAXBContext context = JAXBContext.newInstance(ObjectFromSchema1.class);
final Marshaller m = context.createMarshaller();
m.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
final ObjectFromSchema1 objectFromSchema1 = new ObjectFromSchema1();
objectFromSchema1.setCommonElement1("commonElement1");
objectFromSchema1.setCommonElement2("commonElement2");
objectFromSchema1.setElement1("element1");
objectFromSchema1.setElement2("element2");
m.marshal(objectFromSchema1, System.out);
}
output
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<objectFromSchema1>
<element1>element1</element1>
<element2>element2</element2>
<commonElement1>commonElement1</commonElement1>
<commonElement2>commonElement2</commonElement2>
</objectFromSchema1>
I wrote this custom IDResolver:
final class MyIDResolver extends IDResolver {
Map<String, Course> courses = new HashMap<>();
/**
* #see com.sun.xml.bind.IDResolver#bind(java.lang.String, java.lang.Object)
*/
#Override
public void bind(String id, Object obj) throws SAXException {
if (obj instanceof Course)
courses.put(id, (Course)obj);
else
throw new SAXException("This " + obj.toString() + " cannot found");
}
/**
* #see com.sun.xml.bind.IDResolver#resolve(java.lang.String, java.lang.Class)
*/
#Override
public Callable<?> resolve(final String id, final Class targetType) throws SAXException {
return new Callable<Object>() {
#Override
public Object call() throws Exception {
if (targetType == Course.class)
return courses.get(id);
else
throw new ClassNotFoundException(targetType.toString() + " cannot found");
}
};
}
}
And I have two classes like:
#XmlRootElement(name = "course")
public class Course {
#XmlID
#XmlAttribute
private String id;
#XmlAttribute
private int units;
#XmlAttribute
private Level level;
#XmlAttribute
private String name;
#XmlIDREF
#XmlElement(name = "pre")
private ArrayList<Course> prerequisite;
#XmlIDREF
#XmlElement(name = "co")
private ArrayList<Course> corequisite;
}
#XmlRootElement(name = "dept")
#XmlAccessorType(XmlAccessType.FIELD)
public final class Department {
#XmlAttribute
private String name;
#XmlElementWrapper(name = "courses")
#XmlElement(name = "course")
private ArrayList<Course> courses;
}
And sample XML like this:
<?xml version="1.0" encoding="UTF-8" standalone="yes" ?>
<dept name="ece">
<courses>
<course id="1" units="4" level="UNDERGRADUATE" name="Fundamentals of Programming"/>
<course id="2" units="3" level="UNDERGRADUATE" name="Advanced Programming">
<pre>1</pre>
</course>
</courses>
</dept>
And a simple main like this:
unmarshaller.unmarshal(new FileReader(arg0))
context = JAXBContext.newInstance(Department.class);
unmarshaller = context.createUnmarshaller();
unmarshaller.setProperty(IDResolver.class.getName(), new MyIDResolver());
The resolve() method of a custom IDResolver gets Object.class as targetType. But it should be Course.class. This results in wrong ID resolving. Where is my problem?
You should not use IDResolve when Department contains course. It should be used when Department want to reference to a Course. So for this reason when jaxb arrives a course inside of a department that it contains an element (here <pre>) it will pass object.class to your resolve function.
I'm struggling with some JAXB parsing and need some guidance.
Essentially, I'm trying to add attributes to my class variables that I have already declared as Elements using #XmlElement. So far, any attempt to use #XmlAttribute sets the attribute at the class level.
What I"m currently getting is this:
<DataClass newAttribute="test">
<myElement>I wish this element had an attribute</myElement>
<anotherElement>I wish this element had an attribute too</anotherElement>
</DataClass>
I'd like to to do this:
<DataClass>
<myElement thisAtt="this is what I'm talking about">This is better</myElement>
<anotherElement thisAtt="a different attribute here">So is this</anotherElement>
</DataClass>
I've seen other posts add an attribute to a single element using the #XmlValue, but that doesn't work when you have Elements, and won't work on multiple elements.
Does anyone have a thought on how this could be accomplished?
Thanks!
Jason
This will create that XML:
public class JaxbAttributes {
public static void main(String[] args) throws Exception {
Marshaller marshaller =
JAXBContext.newInstance(DataClass.class).createMarshaller();
StringWriter stringWriter = new StringWriter();
DataClass dataClass = new DataClass(
new Foo("this is what I'm talking about", "This is better"),
new Foo("a different attribute here", "So is this"));
marshaller.marshal(dataClass, stringWriter);
System.out.println(stringWriter);
}
#XmlRootElement(name = "DataClass")
#XmlType(propOrder = {"myElement", "anotherElement"})
static class DataClass {
private Foo myElement;
private Foo anotherElement;
DataClass() {}
public DataClass(Foo myElement, Foo anotherElement) {
this.myElement = myElement;
this.anotherElement = anotherElement;
}
public Foo getMyElement() { return myElement; }
public void setMyElement(Foo myElement) { this.myElement = myElement; }
public Foo getAnotherElement() { return anotherElement; }
public void setAnotherElement(Foo anotherElement) { this.anotherElement = anotherElement; }
}
static class Foo {
private String thisAtt;
private String value;
Foo() {}
public Foo(String thisAtt, String value) {
this.thisAtt = thisAtt;
this.value = value;
}
#XmlAttribute
public String getThisAtt() { return thisAtt; }
public void setThisAtt(String thisAtt) { this.thisAtt = thisAtt; }
#XmlValue
public String getValue() { return value; }
public void setValue(String value) { this.value = value; }
}
}
Note: I'm the EclipseLink JAXB (MOXy) lead, and a member of the JAXB 2.X (JSR-222) expert group.
Alternatively you could use the #XmlPath extension in MOXy to handle this use case:
DataClass
The #XmlPath annotation can be used with the standard JAXB annotations:
import javax.xml.bind.annotation.XmlRootElement;
import javax.xml.bind.annotation.XmlType;
import org.eclipse.persistence.oxm.annotations.XmlPath;
#XmlRootElement(name="DataClass")
#XmlType(propOrder={"myElement", "anotherElement"})
public class DataClass {
private String myElement;
private String myElementThisAtt;
private String anotherElement;
private String anotherElementThisAtt;
public String getMyElement() {
return myElement;
}
public void setMyElement(String myElement) {
this.myElement = myElement;
}
#XmlPath("myElement/#thisAtt")
public String getMyElementThisAtt() {
return myElementThisAtt;
}
public void setMyElementThisAtt(String myElementThisAtt) {
this.myElementThisAtt = myElementThisAtt;
}
public String getAnotherElement() {
return anotherElement;
}
public void setAnotherElement(String anotherElement) {
this.anotherElement = anotherElement;
}
#XmlPath("anotherElement/#thisAtt")
public String getAnotherElementThisAtt() {
return anotherElementThisAtt;
}
public void setAnotherElementThisAtt(String anotherElementThisAtt) {
this.anotherElementThisAtt = anotherElementThisAtt;
}
}
Demo
import javax.xml.bind.JAXBContext;
import javax.xml.bind.Marshaller;
public class Demo {
public static void main(String[] args) throws Exception {
JAXBContext jc = JAXBContext.newInstance(DataClass.class);
DataClass dataClass = new DataClass();
dataClass.setMyElement("This is better");
dataClass.setMyElementThisAtt("this is what I'm talking about");
dataClass.setAnotherElement("So is this");
dataClass.setAnotherElementThisAtt("a different attribute here");
Marshaller marshaller = jc.createMarshaller();
marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
marshaller.marshal(dataClass, System.out);
}
}
Output
<?xml version="1.0" encoding="UTF-8"?>
<DataClass>
<myElement thisAtt="this is what I'm talking about">This is better</myElement>
<anotherElement thisAtt="a different attribute here">So is this</anotherElement>
</DataClass>
More Information
http://bdoughan.blogspot.com/2010/07/xpath-based-mapping.html
http://bdoughan.blogspot.com/2011/05/specifying-eclipselink-moxy-as-your.html