xmlns attribute missing for element when marshalling with JAXB - java

Currently xml generated after marshalling which is missing the xmlns attribute for ColumnAlias:
<ns5:Environment xmlns:ns2="http://www.analytixds.com/amm/xml/systemmanager/v1/extendedproperties" xmlns:ns3="http://www.analytixds.com/amm/xml/systemmanager/v1/columns" xmlns:ns4="http://www.analytixds.com/amm/xml/systemmanager/v1/tables" xmlns:ns5="http://www.analytixds.com/amm/xml/systemmanager/v1/environments">
<ns4:Tables version="12">
<ns4:Table>
<ns3:Columns version="12">
<ns3:Column>
<ColumnAlias></ColumnAlias>
I am trying to set xmlns attribute for ColumnAlias Element so that output would be
<ns5:Environment xmlns:ns2="http://www.analytixds.com/amm/xml/systemmanager/v1/extendedproperties" xmlns:ns3="http://www.analytixds.com/amm/xml/systemmanager/v1/columns" xmlns:ns4="http://www.analytixds.com/amm/xml/systemmanager/v1/tables" xmlns:ns5="http://www.analytixds.com/amm/xml/systemmanager/v1/environments">
<ns4:Tables version="12">
<ns4:Table>
<ns3:Columns version="12">
<ns3:Column>
<ColumnAlias xmlns="http://www.stuff.com"></ColumnAlias>
My Domain class:
#XmlRootElement(name = "Column")
public class Column {
#XmlElement(name = "ColumnAlias", required = true,namespace="http://www.stuff.com")
protected String columnAlias;

I suppose it's common that namespace="http://www.stuff.com" must add namespace to its parent element "Column" and appropriate prefix to child element "columnAlias".

So turns out the xml that was being produced was also valid wrt to the schema and did not need the extra xmlns="http://www.stuff.com".

Related

Jaxb marshal different namespaces with recursive one

I want to marshal into xml like this:
<?xml version='1.0' encoding='UTF-8'?>
<ns1:rootElement xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="some_location" xmlns:ns1="namespace1"
xmlns:ns2="namespace2">
<ns1:firstElement>some text</ns1:firstElement>
<ns1:secondElement>
<ns2:otherElement>some text</ns2:otherElement>
...
<ns2:nElement>
<ns2:innerElement>
<ns2:otherInnerElement>some text</ns2:otherInnerElement>
</ns2:innerElement>
</ns2:nElement>
</ns1:secondElement>
</ns1:rootElement>
There are 2 namespaces. The first one is only for rootElement and direct root element. The second namespace is for other huge number of elements with recursion.
How can I describe this without annotating each elements (tags) for namespace2?
I put root class in one package with package-info like that:
#XmlSchema(
namespace = "namespace1",
elementFormDefault = XmlNsForm.QUALIFIED,
xmlns = {
#XmlNs(prefix = "ns1", namespaceURI = "namespace1")
}
)
And other classes I put in another package with own package-info:
#XmlSchema(
namespace = "namespace2",
elementFormDefault = XmlNsForm.QUALIFIED,
xmlns = {
#XmlNs(prefix = "ns2", namespaceURI = "namespace2")
}
)

unable to parse a node from xml string with dom4j

I'm parsing a xml string with dom4j and I'm using xpath to select some element from it, the code is :
String test = "<?xml version=\"1.0\" encoding=\"UTF-8\"?><epp xmlns=\"urn:ietf:params:xml:ns:epp-1.0\"><response><result code=\"1000\"><msg lang=\"en-US\">Command completed successfully</msg></result><trID><clTRID>87285586-99412370</clTRID><svTRID>52639BB8-1-ARNES</svTRID></trID></response></epp>";
SAXReader reader = new SAXReader();
reader.setIncludeExternalDTDDeclarations(false);
reader.setIncludeInternalDTDDeclarations(false);
reader.setValidation(false);
Document xmlDoc;
try {
xmlDoc = reader.read(new StringReader(test));
xmlDoc.getRootElement();
Node nodeStatus = xmlDoc.selectSingleNode("//epp/response/result");
System.out.print(nodeStatus.getText());
} catch (DocumentException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
I always get null for the nodeStatus variable. I actualy nead to read the code from the result noad from the xml
<result code="1000">
This is the XML that I am reading from the String test:
<?xml version="1.0" encoding="UTF-8"?>
<epp xmlns="urn:ietf:params:xml:ns:epp-1.0">
<response>
<result code="1000">
<msg lang="en-US">Command completed successfully</msg>
</result>
<trID>
<clTRID>87285586-99412370</clTRID>
<svTRID>52639BB8-1-ARNES</svTRID>
</trID>
</response>
</epp>
Any hints?
Your XML has a namespace. DOM4J returns null because it won't find your nodes.
To make it work, you first have to register the namespaces you are using. You will need a prefix. Any one. And you will have to use that prefix in your XPath.
You could use tns for "target namespace". Then you have to create a xpath object with it like this:
XPath xpath = new DefaultXPath("/tns:epp/tns:response/tns:result");
To register the namespaces you will need to create a Map, add the namespace with the prefix you used in the xpath expression, and pass it to the setNamespaceURIs() method.
namespaces.put("tns", "urn:ietf:params:xml:ns:epp-1.0");
xpath.setNamespaceURIs(namespaces);
Now you can call selectSingleNode, but you will call it on your XPath object passing the document as the argument:
Node nodeStatus = xpath.selectSingleNode(xmlDoc);
From there you can extract the data you need. getText() won't give you the data you want. If you want the contents of the result node as XML, you can use:
nodeStatus.asXML()
Edit: to retrieve just the code, change your XPath to:
/tns:epp/tns:response/tns:result/#code
And retrieve the result with
nodeStatus.getText();
I replaced the double slash // (which means descendant-or-self) with / since the expression contains the full path and / is more efficient. But if you only have one result node in your whole file, you can use:
//result/#code
to extract the data. It will match all descendants. If there is more than one result, it will return a node-set.

Parsing XML File with JAXB based on attribute values

I have a Maven and Spring based Java web application.
I have a config.xml file in src/main/resources.
<?xml version="1.0" encoding="UTF-8"?>
<sourceConfigs>
<areas>
<area name="Defects">
<fileHeaders>ID,Issue Key,Fields
</fileHeaders>
</area>
<area name="Organization">
<fileHeaders>ID,Org Key,Fields
</fileHeaders>
</area>
</areas>
<sourceTypes>
<source name="source1">
<adapterObject>source1Adapter</adapterObject>
<resultObject>JsonObject</resultObject>
</source>
<source name="source2">
<adapterObject>source2Adapter</adapterObject>
<resultObject>sourceObject</resultObject>
</source>
</sourceTypes>
</sourceConfigs>
I want to parse the above XML to Java object based on the attributes.
I have created two classes.
Area.java
#XmlRootElement(name="area")
public class Area {
String name;
String fileHeaders;
#XmlAttribute
//get Name
//set Name
#XmlElement
//get fileHeaders
//set FileHeaders
}
Source.java
#XmlRootElement(name="source")
public class Source {
String name;
String adapterObject;
String resultObject;
#XmlAttribute
//get Name
//set Name
#XmlElement
//get adapterObject
//set adapterObject
#XmlElement
//get resultObject
//set resultObject
}
I want to parse the XML based on the attribute value.
ie; if the attribute value of area is Defects, the parsed object should have the values based on that, else if its Organization, then the values based on that and object type to Area object. Similarly for Source type also.
How can I do that?
When it was only a simple XML file like following
<?xml version="1.0" encoding="UTF-8"?>
<sourceConfig area="Defects">
<adapterObject>jAdapter</adapterObject>
<resultObject>jsonObject</resultObject>
</sourceConfig>
My POJO was based on that and the code I used to parse is
public SourceConfig getConfigObject() throws JAXBException, IOException {
JAXBContext jaxbContext = JAXBContext.newInstance(SourceConfig.class);
Unmarshaller jaxbUnmarshaller = jaxbContext.createUnmarshaller();
Resource resource=new ClassPathResource("config.xml");
File file=resource.getFile();
SourceConfig sourceConfig = (SourceConfig) jaxbUnmarshaller.unmarshal(file);
return sourceConfig;
}
But for this complex I don't know how to parse based on attribute values, and also multiple list of data.
How to parse based on the attribute values?
I have created two POJOs for parsing different kind. If it's a single class also it's fine.
UPDATE 1
My expected output.
When I pass the value "Defects" when unmarshalling the Area object, The values should be
name= Defects
fileHeaders=ID,Issue Key,Fields
And If I pass "Organization", The area object values should be based on that. Similarly when unmarshalling source.
Now I am getting as List of Areas and List of SourceTypes, then I take the corresponding object by checking the value.
May be is there any way to parse only selected one based on attribute value instead of getting list and then checking value and returning the object?

JAXB Unmarshalling an subset of Unknown XML content

I have a requirement to unmarshall a subset of Unknown XML content, with that unmarshalled object, I need modify some contents and re-bind the same XML content(subset) with the Original XML.
Sample Input XML:
<Message>
<x>
</x>
<y>
</y>
<z>
</z>
<!-- Need to unmarshall this content to "Content" - java Object -->
<Content>
<Name>Robin</Name>
<Role>SM</Role>
<Status>Active</Status>
</Content>
.....
</Message>
Need to unmarshall the <Content> tag alone, by keeping the other XML part as same. Need to modify the elements in <Content> tag and bind the modified XML part with the original as shown below:
Expected Output XML:
<Message>
<x>
</x>
<y>
</y>
<z>
</z>
<!-- Need to unmarshall this content to "Content" - java Object -->
<Content>
<Name>Robin_123</Name>
<Role>Senior Member</Role>
<Status>1</Status>
</Content>
.....
</Message>
My Questions:
What is the possible solution for this Requirement ? (Except DOM parsing - as XML contnet is very huge)
Is there any option to do this in JAXB2.0 ?
Please provide your suggestions on this.
Consider cutting your source document down to size using the StAX API.
For the given sample, this code creates a DOM document with a root element of the Content element:
class ContentFinder implements StreamFilter {
private boolean capture = false;
#Override public boolean accept(XMLStreamReader xml) {
if (xml.isStartElement() && "Content".equals(xml.getLocalName())) {
capture = true;
} else if (xml.isEndElement() && "Content".equals(xml.getLocalName())) {
capture = false;
return true;
}
return capture;
}
}
XMLInputFactory inFactory = XMLInputFactory.newFactory();
XMLStreamReader reader = inFactory.createXMLStreamReader(inputStream);
reader = inFactory.createFilteredReader(reader, new ContentFinder());
Source src = new StAXSource(reader);
DOMResult res = new DOMResult();
TransformerFactory.newInstance().newTransformer().transform(src, res);
Document doc = (Document) res.getNode();
This can then be passed to JAXB as a DOMSource.
Similar techniques can be used when rewriting the XML on output.
JAXB doesn't seem to accept a StreamSource directly, at least in the Oracle 1.7 implementation.
You can annotate an Object property on your class with #XmlAnyElement and by default the unmapped content will be captured as a DOM nodes. If you specify a DomHandler on the #XmlAnyElement then you can control the format. Here is a link to an example where the content is kept as a String.
JAXB use String as it is

Dom4j xmlns attribute

I want to add xmlns attribute to the root node only, however when i add a namespace to the root element, all subsequent child elements also get the same xmlns attribute. How do I add xmlns attribute to a single node but not any of its children ?
CODE:
public String toXml() {
Document document = DocumentHelper.createDocument();
Element documentRoot = document.addElement("ResponseMessage");
documentRoot.addNamespace("",getXmlNamespace())
.addAttribute("xmlns:xsi", getXmlNamespaceSchemaInstance())
.addAttribute("xsi:schemaLocation", getXmlSchemaLocation())
.addAttribute("id", super.getId());
Element header = documentRoot.addElement("Header");
buildHeader(header);
Element body = documentRoot.addElement("Body");
buildProperties(body);
body.addElement("StatusMessage").addText(this.getStatusMessage().getMessage());
return document.asXML();
}
OK, new answer.
If you want your elements to belong to a certain namespace, be sure to create them in that namespace. Use the methods that have Qname as one of its arguments. If you create an element with no namespace, DOM4J will have to add namespace declarations to accommodate to your (unwillingly) specification.
Your example slightly edited. I didn't use QName, but gave each element a namespace uri:
public static String toXml() {
Document document = DocumentHelper.createDocument();
Element documentRoot = document.addElement("ResponseMessage",
getXmlNamespace());
documentRoot.addAttribute(QName.get("schemaLocation", "xsi", "xsi-ns"),
"schema.xsd").addAttribute("id", "4711");
Element header = documentRoot.addElement("Header");
Element body = documentRoot.addElement("Body", getXmlNamespace());
// buildProperties(body);
body.addElement("StatusMessage", getXmlNamespace()).addText("status");
return document.asXML();
}
private static String getXmlNamespace() {
return "xyzzy";
}
public static void main(String[] args) throws Exception {
System.out.println(toXml());
}
produces as output:
<?xml version="1.0" encoding="UTF-8"?>
<ResponseMessage xmlns="xyzzy" xmlns:xsi="xsi-ns" xsi:schemaLocation="schema.xsd" id="4711">
<Header/><Body><StatusMessage>status</StatusMessage></Body>
</ResponseMessage>
UPDATE 2:
Note also, that I changed the way how the schemaLocation attribute is declared. You really never have to manually manage the namespace declarations--this will be taken care of by the library.
However, there is one case where it might be useful to add a namespace delaration: If you have a document with predominantly namespace X elements, and some child elements with namspace Y spread out in the document, declaring a namesapce binding for Y at the root element, may save a lot of repeating name space declarations in the child elements.
Heres how. Its a bit of a hack, but it does what you want:
public static String toXml() {
Document d = DocumentHelper.createDocument();
Namespace rootNs = new Namespace("", DEFAULT_NAMESPACE); // root namespace uri
Namespace xsiNs = new Namespace("xsi", XSI_NAMESPACE); // xsi namespace uri
QName rootQName = QName.get(rootElement, rootNs); // your root element's name
Element root = d.addElement(rootElement);
root.setQName(rootQName);
root.add(xsiNs);
root.addAttribute("xsi:schemaLocation", SCHEMA_LOC)
.addAttribute("id", super.getId());
Element header = documentRoot.addElement("Header");
Element body = documentRoot.addElement("Body", getXmlNamespace());
// buildProperties(body);
body.addElement("StatusMessage", getXmlNamespace()).addText("status");
return document.asXML();
}

Categories