I'm working on an xml standard that requires that the following root element must be defined:
<ClinicalDocument xsi:schemaLocation=”urn:hl7 org:v3 CDA.xsd” xmlns=”urn:hl7-
org:v3” xmlns:xsi=”http://www.w3.org/2001/XMLSchema-instance”>
Now, I'm using java.xml.bind. Usually I annotate each class and then I use Marshallers and Unmarshallers to write/read valid xml files.
"My idea" was to annotate the package-info.java to specify the xsi:schemaLocation, xmlns and xmlns:xsi properties of ClinicalDocument. However, I can only insert the last property (xmlns:xsi), while I have no idea of how to render the first and furthermore the second is rendered as xmlns:ns3.
Here is my code in package-info.java:
#javax.xml.bind.annotation.XmlSchema (
xmlns = {
#javax.xml.bind.annotation.XmlNs(prefix="",
namespaceURI="urn:hl7-org:v3"),
#javax.xml.bind.annotation.XmlNs(prefix="xsi",
namespaceURI="http://www.w3.org/2001/XMLSchema-instance")
}
)
package foo;
Here is my class ClinicalDocument.java in package foo:
package foo;
#XmlRootElement(name="ClinicalDocument")
public class ClinicalDocument {....}
And finally is what I get with the Marshaller:
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<ClinicalDocument xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:ns3="urn:hl7-org:v3">
...
</ClinicalDocument>
So, I have to create and read valid xml file under the three properties shown above. Any idea?
The only valid solution that I found is to add:
#XmlAttribute(name="xsi:schemaLocation")
protected final String xsi_schemaLocation="urn:hl7 org:v3 CDA.xsd";
#XmlAttribute(name="xmlns")
protected final String xmlns="urn:hl7-org:v3";
#XmlAttribute(name="xmlns:xsi")
protected final String xmlns_xsi="http://www.w3.org/2001/XMLSchema instance";
in class ClinicalDocument.
It works, but I don't like it! I would like to use annotation at package level.
Supporting the annotations is only the beginning of the requirements for reading and writing CDA documents - I would recommend the use of MDHT, open source project with an API to create, consume and validate CDA documents.
You can find the project here
https://www.projects.openhealthtools.org/sf/projects/mdht/
Related
So I have a specific Schema annotation I need to be added to the package-info.java. I tried around a few things and searched for an answer online, but couldn't find any that apply to my problem.
packag-info.java:
#javax.xml.bind.annotation.XmlSchema(
...
)
package my.package.name;
XmlSchema I want:
<databaseChangeLog
xmlns="http://www.liquibase.org/xml/ns/dbchangelog"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog-ext
http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-ext.xsd http://www.liquibase.org/xml/ns/pro
http://www.liquibase.org/xml/ns/pro/liquibase-pro-4.1.xsd http://www.liquibase.org/xml/ns/dbchangelog
http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-4.1.xsd">
</databaseChangeLog>
My question is, what has to go inside the #javax.xml.bind.annotation.XmlSchema(...), to achieve this schema?
For anyone having a similar problem, I found the solution.
The Schema here has two components which have to be set to in your code to generate,
the namespace:
xmlns="http://www.liquibase.org/xml/ns/dbchangelog"
the schemaLocation:
xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog-ext
http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-ext.xsd http://www.liquibase.org/xml/ns/pro
http://www.liquibase.org/xml/ns/pro/liquibase-pro-4.1.xsd http://www.liquibase.org/xml/ns/dbchangelog
http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-4.1.xsd"
The namespace has to be set inside the package-info.java using javax
#javax.xml.bind.annotation.XmlSchema(
namespace = "http://www.liquibase.org/xml/ns/dbchangelog",
elementFormDefault = javax.xml.bind.annotation.XmlNsForm.QUALIFIED)
package de.softproject.x4.server.migration.keycloak.changelog_beans;
But the schemaLocation can't be set here, or at least I haven't found out how. What works is to set it with jaxb directly (I am using jaxb as my Java XML Binding Framework, if you're using another, this solution won't apply)
JAXBContext jaxbContext = JAXBContext.newInstance(ClassToHaveSchema.class);
Marshaller marshaller = jaxbContext.createMarshaller();
marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
marshaller.setProperty(Marshaller.JAXB_SCHEMA_LOCATION,
"http://www.liquibase.org/xml/ns/dbchangelog-ext
http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-
ext.xsd http://www.liquibase.org/xml/ns/pro
http://www.liquibase.org/xml/ns/pro/liquibase-pro-4.1.xsd
http://www.liquibase.org/xml/ns/dbchangelog
http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-
4.1.xsd");
The important part here is the marshaller.setProperty(Marshallar.JAXB_SCHEMA_LOCATION, ...). When you set up your JAXB Marshaller with the class you want to marshall, this will add the xsi:schemaLocation you specified and automatically generate the xmlns:xsi for you (the standard namespace prefix xsi)
The resulting XML will then have the schema set like this:
<ClassToHaveSchema
xmlns="http://www.liquibase.org/xml/ns/dbchangelog"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog-ext
http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-ext.xsd http://www.liquibase.org/xml/ns/pro
http://www.liquibase.org/xml/ns/pro/liquibase-pro-4.1.xsd http://www.liquibase.org/xml/ns/dbchangelog
http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-4.1.xsd">
</ClassToHaveSchema>
Your SchemaLocation may be shorter or longer than here, it doesn't have to be 6 URIs long, it's in this case though.
You could of course create a class containing this information as constants:
public class LiquibaseInfo {
public static final String NAMESPACE = "http://www.liquibase.org/xml/ns/dbchangelog";
public static final String SCHEMA_LOCATION = "http://www.liquibase.org/xml/ns/dbchangelog-ext http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-ext.xsd http://www.liquibase.org/xml/ns/pro http://www.liquibase.org/xml/ns/pro/liquibase-pro-4.1.xsd http://www.liquibase.org/xml/ns/dbchangelog http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-4.1.xsd";
private LiquibaseInfo() {}
}
And then use the constants in your code, which would make it a little bit more understandable:
...
namespace = LiquibaseInfo.NAMESPACE
...
...
marshaller.setProperty(Marshaller.JAXB_SCHEMA_LOCATION, LiquibaseInfo.SCHEMA_LOCATION);
...
CAUTION:
When setting the namespace inside the package-info this package now is linked to this namespace. Meaning, if you for example unmarshall some XML file inside some class in this package, which uses another namespace, it will not work, because it expects the set namespace. Happend to me, can be fixed easily, by changing up the packages better, but so you don't have to ponder over your code not working.
I have an XSD with an element - it is the XMLRootElement if that makes a difference - like this:
<xsd:element name= "SomeElement">
I need to have the generated Java class have a custom XML element name while keeping the default Java class name, so the generated class needs to look like this:
#XmlRootElement(name = "fo:SomeElement")
public class SomeElement
So that marshal/unmarshalled xml elements will show as
<fo:SomeElement>
Can someone help me out with what I need to change to either the XSD file or the binding file?
First of all, with your question you opened a big can of worms.
Things are more complicated than you thought they would be.
To fully understand the rest of this answer
you will surely need to learn more about the namespace concept in XML,
for example at w3schools.com - XML Namespaces.
Having said that, the following stuff should give a quick entry into that topic.
Note that fo:SomeElement is not directly an XML element name.
The fo: is a so-called namespace-prefix.
The namespace-prefix needs to be mapped to a namespace-URI by xmlns:fo="...",
By convention fo: is the namespace-prefix used for XSL Formatting Objects.
Therefore most probably your XML file will look like this:
<fo:SomeElement xmlns:fo="http://www.w3.org/1999/XSL/Format" ...>
...
</fo:SomeElement>
Note that "http://www.w3.org/1999/XSL/Format" is the namespace-URI
as specified in the XSL Formatting Objects specification.
Note also, that namespace-prefixes (here fo) by themselves are irrelevant
and were only invented to make the XML content easier to read for humans.
So instead of fo you might as well have used bla in all places as the namespace-prefix,
and the XML content still would have the exact same meaning.
The only relevant things are the namespace-URIs (here "http://www.w3.org/1999/XSL/Format").
With JAXB the correct Java root class would look like this.
Note the namespace given in the #XmlRootElement annotation.
#XmlRootElement(name="SomeElement", namespace="http://www.w3.org/1999/XSL/Format")
public class SomeElement {
...
}
You would need to specify this namespace-URI not only in #XmlRootElement,
but also for nested Java properties corresponding to any <fo:something> XML content.
Fur this purpose most JAXB annotations (#XmlElement, #XmlAttribute, ...)
can accept a namespace parameter as well.
The XML schema definition (XSD) consistent with the XML example and
the Java class above would look like this.
Note the targetNamespace given in the <xs:schema> element.
<xs:schema version="1.0" targetNamespace="http://www.w3.org/1999/XSL/Format"
xmlns:xs="http://www.w3.org/2001/XMLSchema">
<xs:element name="SomeElement">
...
</xs:element>
</xs:schema>
I use Properties#storeToXML to convert java.util.Properties to XML format. However, by default it generates XML with DTD schema for it:
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!DOCTYPE properties SYSTEM "http://java.sun.com/dtd/properties.dtd">
<properties>
...
</properties>
Is it possible to use XSD schema for it instead? How can I reconfigure it?
Using the default classes, there seems to be no way of changing a doctype declaration to a XSD-based approach.
But there seems to be a plugin way of interventing into the XML storing behavior (at least in Java 8): The method Properties.storeToXml internally delegates to a loaded XmlPropertiesProvider (from package sun.util.spi).
This properties provider is either loaded by inspecting the system property "sun.util.spi.XmlPropertiesProvider" or (if not found) by loading it with the service loader mechanism.
With this approach you can implement a XmlPropertiesProvider yourself (it's an abstract class with the methods load and store) and do those XML parts in your own way.
Since at least May, 2016, the Properties class uses jdk.internal.util.xml.PropertiesDefaultHandler and the following hard-coded object instantiation to both store and load XML:
PropertiesDefaultHandler handler = new PropertiesDefaultHandler();
This means the following code will no longer work to set the default handler for XML-based properties:
System.setProperty(
"sun.util.spi.XmlPropertiesProvider",
XmlPropertiesTransformer.class.getCanonicalName()
);
There does not appear to be a way to introduce a different handler because the PropertiesDefaultHandler class does not permit injection of custom handlers.
How would one unmarshall the following XML response with JAXB into a domain class:
<?xml version="1.0" encoding="UTF-8"?>
<time>2014-01-14T06:24:34+00:00</time>
My first instinct was to use something like (short version):
#XmlRootElement
public class Time {
#XmlElement
public Date time;
}
but I think JAXB then sees 2 elements with the Time name. I also tried without using the #XmlRootElement annotation, but to no avail.
Have you tried using #XmlValue instead of #XmlElement for the time field? After all, it is the value of the root element, rather than a sub-element.
I've now tried this with the file supplied, and it works properly.
I want to map following xml using custom name sapace. I checked How to have custom namespace prefix but could not find any answer.
<p385:execute xmlns:p385="http://tal.myserver.com">
<version xsi:type="xsd:string">0.1.0</version>
<xmlData xsi:type="xsd:string">
.... xml encoded data
</xmlData>
</p385:execute>
How can i map this to a java class?
Since it is only the root element that is namespace qualified, you just need to specify the namespace on the #XmlRootElement annotation for the class.
#XmlRootElement(namespace="http://tal.myserver.com")
public class Execute {
}
You can the suggest the prefix that should be used for the namespace using the package level #XmlSchema annotation:
http://blog.bdoughan.com/2011/11/jaxb-and-namespace-prefixes.html
Use the wsimport tool to generate artifacts such as JAXB classes from a WSDL:
http://docs.oracle.com/javase/7/docs/technotes/tools/share/wsimport.html
http://jax-ws-commons.java.net/jaxws-maven-plugin/wsimport-mojo.html