Xml Element with Attribute and Content Using Jaxb

How to get xml attribute using JAXB

Bank

You need to change the property property from a String to a domain object.

@XmlAccessorType(XmlAccessType.FIELD)
public class Bank {
private String description;
private String externalkey;
private Property property;
}

Property

Then your Property object would look something like:

@XmlAccessorType(XmlAccessType.FIELD)
public class Property {

@XmlAttribute
private String name;

@XmlAtrribute
private String value;

}

JAXB - How to create the XML element with attributes from Map?

You could use a custom adapter for this. Example

//Token.java
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import javax.xml.bind.annotation.XmlAttribute;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlRootElement;
import javax.xml.bind.annotation.XmlSeeAlso;
import javax.xml.bind.annotation.adapters.XmlAdapter;
import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter;

@XmlRootElement
class LowLevelToken {
@XmlAttribute(name = "info-key")
public String key;
@XmlAttribute(name = "info-value")
public String value;

private LowLevelToken() {}

public LowLevelToken(String key, String value) {
this.key = key;
this.value = value;
}
}

@XmlRootElement
class HighLevelToken {
@XmlAttribute(name = "info-1")
public String info1;
@XmlAttribute(name = "info-2")
public String info2;

private HighLevelToken() {}

public HighLevelToken(String info1, String info2) {
this.info1 = info1;
this.info2 = info2;
}
}

class TokenWrapper {
@XmlElement(name="LowLevel")
public List<LowLevelToken> tokens = new ArrayList<LowLevelToken>();
}

class TokenAdapter extends XmlAdapter<TokenWrapper, Map<String, String>> {
@Override
public TokenWrapper marshal(Map<String, String> lowlevelTokens)
throws Exception {
TokenWrapper wrapper = new TokenWrapper();
List<LowLevelToken> elements = new ArrayList<LowLevelToken>();
for (Map.Entry<String, String> property : lowlevelTokens.entrySet()) {
elements.add(new LowLevelToken(property.getKey(), property.getValue()));
}
wrapper.tokens = elements;
return wrapper;
}

@Override
public Map<String, String> unmarshal(TokenWrapper tokenWrapper) throws Exception {
Map<String, String> tokens = null;
if(tokenWrapper != null && tokenWrapper.tokens != null && !tokenWrapper.tokens.isEmpty()){
tokens = new HashMap<String, String>();
for(LowLevelToken token : tokenWrapper.tokens){
tokens.put(token.key, token.value);
}
}
return tokens;
}
}

@XmlRootElement(name = "Token")
public class Token {

HighLevelToken highLevel;
Map<String, String> lowLevel;

public HighLevelToken getHighLevel() {
return highLevel;
}

@XmlElement(name = "HighLevel")
public void setHighLevel(HighLevelToken highLevel) {
this.highLevel = highLevel;
}

public Map<String, String> getLowLevel() {
return lowLevel;
}

@XmlElement(name = "LowLevel")
@XmlJavaTypeAdapter(TokenAdapter.class)
public void setLowLevel(Map<String, String> lowLevel) {
this.lowLevel = lowLevel;
}
}

A sample program

import java.util.HashMap;

import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBException;
import javax.xml.bind.Marshaller;

public class JAXBExample {
public static void main(String[] args) {
Token token = new Token();
token.setHighLevel(new HighLevelToken("1", "2"));
token.setLowLevel(new HashMap<String, String>() {{ put("LK1", "LV1"); put("LK2", "LV2"); put("LK2", "LV2"); }});
try {
JAXBContext jaxbContext = JAXBContext.newInstance(Token.class);
Marshaller jaxbMarshaller = jaxbContext.createMarshaller();
jaxbMarshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
jaxbMarshaller.marshal(token, System.out);
} catch (JAXBException e) {
e.printStackTrace();
}
}
}

This generates

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<Token>
<HighLevel info-1="1" info-2="2"/>
<LowLevel>
<LowLevel info-key="LK2" info-value="LV2"/>
<LowLevel info-key="LK1" info-value="LV1"/>
</LowLevel>
</Token>

Using JAXB, how can I unmarshal an XMLelement with an attribute-defined type to an object based on that attribute?

You have at least two options. First, you can map the "class" attribute to an enumeration of strings and implement instantiation logic in the #getBaz() method of your TargetedMessage. The XSD would be as shown below (rename types as necessary):

<?xml version="1.0" encoding="UTF-8"?>
<schema xmlns="http://www.w3.org/2001/XMLSchema" targetNamespace="http://www.example.org/XMLSchema" xmlns:tns="http://www.example.org/XMLSchema" elementFormDefault="qualified">

<include schemaLocation=""></include>

<simpleType name="simpleTypeClass">
<restriction base="string">
<enumeration value="class.path.from.external.application.Foo"></enumeration>
<enumeration value="class.path.from.external.application.Bar"></enumeration>
</restriction>
</simpleType>

<complexType name="payloadType">
<sequence>
<element name="id" type="string"></element>
</sequence>
<attribute name="class" type="tns:simpleTypeClass"></attribute>
</complexType>

<complexType name="targetedMessageType">
<sequence>
<element name="sender" type="string"></element>
<element name="payload" type="tns:payloadType"></element>
</sequence>
</complexType>
</schema>

Second, you can transform your initial XML, so as the "payload" element is renamed according to its "class" attribute content. For example, it could be "payloadFoo", "payloadBar", etc. Then, you can map it directly to the desired classes. The respective XSD is shown below.

<?xml version="1.0" encoding="UTF-8"?>
<schema xmlns="http://www.w3.org/2001/XMLSchema" targetNamespace="http://www.example.org/XMLSchema" xmlns:tns="http://www.example.org/XMLSchema" elementFormDefault="qualified">

<simpleType name="simpleTypeFoo">
<restriction base="string">
<enumeration value="class.path.from.external.application.Foo"></enumeration>
</restriction>
</simpleType>

<simpleType name="simpleTypeBar">
<restriction base="string">
<enumeration value="class.path.from.external.application.Bar"></enumeration>
</restriction>
</simpleType>

<complexType name="payloadTypeFoo">
<sequence>
<element name="id" type="string"></element>
</sequence>
<attribute name="class" type="tns:simpleTypeFoo"></attribute>
</complexType>

<complexType name="payloadTypeBar">
<sequence>
<element name="id" type="string"></element>
</sequence>
<attribute name="class" type="tns:simpleTypeBar"></attribute>
</complexType>

<complexType name="payloadType">
<choice>
<element name="payloadFoo" type="tns:payloadTypeFoo" minOccurs="0" maxOccurs="1"></element>
<element name="payloadBar" type="tns:payloadTypeBar" minOccurs="0" maxOccurs="1"></element>
</choice>
</complexType>

<complexType name="targetedMessageType">
<sequence>
<element name="sender" type="string"></element>
<element name="payload" type="tns:payloadType"></element>
</sequence>
</complexType>
</schema>

JAXB Adding attributes to a XmlElement for simple data types

You could use EclipseLink JAXB (MOXy)'s @XmlPath annotation to solve this problem (I'm the MOXy tech lead):

Notifications

import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlRootElement;

import org.eclipse.persistence.oxm.annotations.XmlPath;

@XmlRootElement
@XmlAccessorType(XmlAccessType.FIELD)
public class Notifications {

private String date;

@XmlPath("subject/@creditcard_num")
private String creditcardNum;

@XmlPath("subject/@checknum")
private String checknum;

private String subject;

@XmlPath("body/@payment_amount")
private String paymentAmount;

@XmlPath("body/@return_status")
private String returnStatus;

private String body;

}

jaxb.properties

To use MOXy as your JAXB implementation you need to put a file named jaxb.properties in the same package as your model classes with the following entry:

javax.xml.bind.context.factory=org.eclipse.persistence.jaxb.JAXBContextFactory

Demo

import java.io.File;

import javax.xml.bind.JAXBContext;
import javax.xml.bind.Marshaller;
import javax.xml.bind.Unmarshaller;

public class Demo {

public static void main(String[] args) throws Exception {
JAXBContext jc = JAXBContext.newInstance(Notifications.class);

Unmarshaller unmarshaller = jc.createUnmarshaller();
Notifications notifications = (Notifications) unmarshaller.unmarshal(new File("input.xml"));

Marshaller marshaller = jc.createMarshaller();
marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
marshaller.marshal(notifications, System.out);
}

}

For More Information:

  • http://bdoughan.blogspot.com/2010/07/xpath-based-mapping.html
  • http://bdoughan.blogspot.com/2010/09/xpath-based-mapping-geocode-example.html
  • http://bdoughan.blogspot.com/2011/03/map-to-element-based-on-attribute-value.html

JAXB Unmarshalling having same XML elements and same attribute names but different attribute values into different Java objects

I looked at the question again and got an understanding of what you are asking and the following is the code that works perfectly for the XML you have provided. If incase you want some modification you can do accordingly in Child.cass

Parent.class:

@XmlRootElement(name = "parent")
@Data
@XmlAccessorType(XmlAccessType.FIELD)
public class Parent {
@XmlElement(name = "child")
List<Child> child;
}

Child.class:

@Data
@XmlAccessorType(XmlAccessType.NONE)
public class Child {
@XmlAttribute
private String name;

@XmlAttribute
private String reason;

@XmlAttribute
private String action;

@XmlAttribute
List<String> unchangedList;

@XmlAttribute
List<String> newList;

@XmlAttribute
List<String> deleteList;

private void afterUnmarshal(Unmarshaller m, Object parent) {
if (action.equals("Unchanged")) {
unchangedList = new ArrayList<>();
unchangedList.add(action);
} else if (action.equals("New")) {
newList = new ArrayList<>();
newList.add(action);
} else if (action.equals("Delete")) {
deleteList = new ArrayList<>();
deleteList.add(action);
}
action = null;
}
}

JaxBMain.class:

public class JaxBMain {

public static void main(String[] args) throws JAXBException, XMLStreamException {
final InputStream inputStream = Unmarshalling.class.getClassLoader().getResourceAsStream("action.xml");
final XMLStreamReader xmlStreamReader = XMLInputFactory.newInstance().createXMLStreamReader(inputStream);
final Unmarshaller unmarshaller = JAXBContext.newInstance(Parent.class).createUnmarshaller();
final Parent parent = unmarshaller.unmarshal(xmlStreamReader, Parent.class).getValue();
System.out.println(parent.toString());

Marshaller marshaller = JAXBContext.newInstance(Parent.class).createMarshaller();
marshaller.setProperty(Marshaller.JAXB_FRAGMENT, Boolean.TRUE);
marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, Boolean.TRUE);
marshaller.marshal(parent, System.out);
}
}

Following is the output you will get:

Parent(child=[Child(name=John1, reason=12, action=null, unchangedList=[Unchanged], newList=null, deleteList=null), Child(name=John2, reason=12, action=null, unchangedList=[Unchanged], newList=null, deleteList=null), Child(name=John3, reason=12, action=null, unchangedList=null, newList=[New], deleteList=null), Child(name=John4, reason=12, action=null, unchangedList=null, newList=[New], deleteList=null), Child(name=John5, reason=12, action=null, unchangedList=null, newList=null, deleteList=[Delete]), Child(name=John6, reason=12, action=null, unchangedList=null, newList=null, deleteList=[Delete]), Child(name=John8, reason=12, action=null, unchangedList=[Unchanged], newList=null, deleteList=null), Child(name=John9, reason=12, action=null, unchangedList=null, newList=null, deleteList=[Delete]), Child(name=John10, reason=12, action=null, unchangedList=null, newList=[New], deleteList=null)])
<parent>
<child name="John1" reason="12" unchangedList="Unchanged"/>
<child name="John2" reason="12" unchangedList="Unchanged"/>
<child name="John3" reason="12" newList="New"/>
<child name="John4" reason="12" newList="New"/>
<child name="John5" reason="12" deleteList="Delete"/>
<child name="John6" reason="12" deleteList="Delete"/>
<child name="John8" reason="12" unchangedList="Unchanged"/>
<child name="John9" reason="12" deleteList="Delete"/>
<child name="John10" reason="12" newList="New"/>
</parent>

Using JAXB to extract content of several XML elements as text

The @XmlAnyElement annotation is used as a catch-all for elements in the XML input that aren't mapped by name to some specific property. That's why there can be only one such annotation per class (including inherited properties). What you want is this:

public class Item implements Serializable {
private String title;
private String text;

public String getTitle() {
return title;
}
@XmlElement(name = "title")
@XmlJavaTypeAdapter(value = TitleHandler.class)
public void setTitle(String title) {
this.title = title;
}
public String getText() {
return text;
}
@XmlElement(name = "text")
@XmlJavaTypeAdapter(value = TextHandler.class)
public void setText(String text) {
this.text = text;
}
}

The @XmlElement annotation indicates that the corresponding property is mapped to elements with that name. So the Java text property derives from the XML <text> element, and the title property from the <title> element. Since the names of the properties and the elements are the same, this is also the default behavior without the @XmlElement annotations, so you could leave them out.

In order to handle the conversion from XML content to a String instead of an actual structure (like a Title class or Text class) you'll need an adapter. that's what the @XmlJavaTypeAdapter annotation is for. It specifies how marshalling/unmarshalling for that property must be handled.

See this useful answer: https://stackoverflow.com/a/18341694/630136

An example of how you could implement TitleHandler.

import java.io.StringReader;
import java.io.StringWriter;
import javax.xml.bind.annotation.adapters.XmlAdapter;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMResult;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;
import javax.xml.transform.stream.StreamSource;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.NodeList;

public class TitleHandler extends XmlAdapter<Object, String> {

/**
* Factory for building DOM documents.
*/
private final DocumentBuilderFactory docBuilderFactory;
/**
* Factory for building transformers.
*/
private final TransformerFactory transformerFactory;

public TitleHandler() {
docBuilderFactory = DocumentBuilderFactory.newInstance();
transformerFactory = TransformerFactory.newInstance();
}

@Override
public String unmarshal(Object v) throws Exception {
// The provided Object is a DOM Element
Element titleElement = (Element) v;
// Getting the "a" child elements
NodeList anchorElements = titleElement.getElementsByTagName("a");
// If there's none or multiple, return empty string
if (anchorElements.getLength() != 1) {
return "";
}
Element anchor = (Element) anchorElements.item(0);
// Creating a DOMSource as input for the transformer
DOMSource source = new DOMSource(anchor);
// Default transformer: identity tranformer (doesn't alter input)
Transformer transformer = transformerFactory.newTransformer();
// This is necessary to avoid the <?xml ...?> prolog
transformer.setOutputProperty("omit-xml-declaration", "yes");
// Transform to a StringWriter
StringWriter stringWriter = new StringWriter();
StreamResult result = new StreamResult(stringWriter);
transformer.transform(source, result);
// Returning result as string
return stringWriter.toString();
}

@Override
public Object marshal(String v) throws Exception {
// DOM document builder
DocumentBuilder docBuilder = docBuilderFactory.newDocumentBuilder();
// Creating a new empty document
Document doc = docBuilder.newDocument();
// Creating the <title> element
Element titleElement = doc.createElement("title");
// Setting as the document root
doc.appendChild(titleElement);
// Creating a DOMResult as output for the transformer
DOMResult result = new DOMResult(titleElement);
// Default transformer: identity tranformer (doesn't alter input)
Transformer transformer = transformerFactory.newTransformer();
// String reader from the input and source
StringReader stringReader = new StringReader(v);
StreamSource source = new StreamSource(stringReader);
// Transforming input string to the DOM
transformer.transform(source, result);
// Return DOM root element (<title>) for JAXB marshalling to XML
return doc.getDocumentElement();
}

}

If the type for unmarshalling input/marshalling output is left as Object, JAXB will provide DOM nodes. The above uses XSLT transformations (though without an actual stylesheet, just an "identity" transform) to turn the DOM input into a String and vice-versa. I've tested it on a minimal input document and it works for both XML to an Item object and the other way around.

EDIT:

The following version will handle any XML content in <title> rather than expecting a single <a> element. You'll probably want to turn this into an abstract class and then have TitleHander and TextHandler extend it, so that the currently hardcoded <title> tags are provided by the implementation.

import java.io.StringReader;
import java.io.StringWriter;
import javax.xml.bind.annotation.adapters.XmlAdapter;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMResult;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;
import javax.xml.transform.stream.StreamSource;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.NodeList;

public class TitleHandler extends XmlAdapter<Object, String> {

/**
* Factory for building DOM documents.
*/
private final DocumentBuilderFactory docBuilderFactory;
/**
* Factory for building transformers.
*/
private final TransformerFactory transformerFactory;

/**
* XSLT that will strip the root element. Used to only take the content of an element given
*/
private final static String UNMARSHAL_XSLT = "<?xml version=\"1.0\" encoding=\"UTF-8\" ?>\n" +
"<xsl:transform xmlns:xsl=\"http://www.w3.org/1999/XSL/Transform\" version=\"1.0\">\n" +
"\n" +
" <xsl:output method=\"xml\" omit-xml-declaration=\"yes\" />\n" +
"\n" +
" <xsl:template match=\"/*\">\n" +
" <xsl:apply-templates select=\"@*|node()\"/>\n" +
" </xsl:template>\n" +
"\n" +
" <xsl:template match=\"@*|node()\">\n" +
" <xsl:copy>\n" +
" <xsl:apply-templates select=\"@*|node()\"/>\n" +
" </xsl:copy>\n" +
" </xsl:template>\n" +
" \n" +
"</xsl:transform>";

public TitleHandler() {
docBuilderFactory = DocumentBuilderFactory.newInstance();
transformerFactory = TransformerFactory.newInstance();
}

@Override
public String unmarshal(Object v) throws Exception {
// The provided Object is a DOM Element
Element rootElement = (Element) v;
// Creating a DOMSource as input for the transformer
DOMSource source = new DOMSource(rootElement);
// Creating a transformer that will strip away the root element
StreamSource xsltSource = new StreamSource(new StringReader(UNMARSHAL_XSLT));
Transformer transformer = transformerFactory.newTransformer(xsltSource);
// Transform to a StringWriter
StringWriter stringWriter = new StringWriter();
StreamResult result = new StreamResult(stringWriter);
transformer.transform(source, result);
// Returning result as string
return stringWriter.toString();
}

@Override
public Object marshal(String v) throws Exception {
// DOM document builder
DocumentBuilder docBuilder = docBuilderFactory.newDocumentBuilder();
// Creating a new empty document
Document doc = docBuilder.newDocument();
// Creating a DOMResult as output for the transformer
DOMResult result = new DOMResult(doc);
// Default transformer: identity tranformer (doesn't alter input)
Transformer transformer = transformerFactory.newTransformer();
// String reader from the input and source
StringReader stringReader = new StringReader("<title>" + v + "</title>");
StreamSource source = new StreamSource(stringReader);
// Transforming input string to the DOM
transformer.transform(source, result);
// Return DOM root element for JAXB marshalling to XML
return doc.getDocumentElement();
}

}

How to get xml attribute and values using JAXB

I suppose you have your class
MessageMapping.java which has in turn a list (or one? dunno) of message of type Message.java.
Message.java in turn will be structured with a list of Field of type Field.java.
The classes will be as follow:

    @XmlAccessorType(XmlAccessType.FIELD)
public class Field {

@XmlAttribute
private String tag;

@XmlAtrribute
private String source;

@XmlAtrribute
private String tranData;

@XmlAtrribute
private String dataType;

@XmlAtrribute
private String defaultValue;

/*
GETTER AND SETTER HERE
*/

}

And Message.java like this:

        @XmlAccessorType(XmlAccessType.FIELD)
public class Message {
private List<Field> fields;
private String Rtype;
private String direction;
private String name;
private String mode;

/*
GETTER AND SETTER HERE
*/
}

And ultimately message_mapping class will need to be built as you see fit to accomodate a list of message or a single one, dunno what are your specification.
Hope it helps.



Related Topics



Leave a reply



Submit