So here's the scenario...I have an XSD file describing all the objects that I need. I can create the objects in Java using JAXB no problem. I have an XML/RDF file that I need to parse into those objects.
What is the EASIEST way to do this?
I have been looking into Jena and have played around with it, but can't see how to easily map the XML/RDF file to the XSD objects that were generated. Here is a snippet of the XSD file as well as the XML/RDF file:
<?xml version="1.0" encoding="UTF-8"?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"
xmlns:a="http://langdale.com.au/2005/Message#"
xmlns:sawsdl="http://www.w3.org/ns/sawsdl"
targetNamespace="http://iec.ch/TC57/2007/profile#"
elementFormDefault="qualified"
attributeFormDefault="unqualified"
xmlns="http://langdale.com.au/2005/Message#"
xmlns:m="http://iec.ch/TC57/2007/profile#">
<xs:annotation/>
<xs:element name="Profile" type="m:Profile"/>
<xs:complexType name="Profile">
<xs:sequence>
<xs:element name="Breaker" type="m:Breaker" minOccurs="0" maxOccurs="unbounded"/>
And the XML/RDF:
<!-- CIM XML Output For switch783:(295854688) -->
<cim:Switch rdf:ID="Switch_295854688">
<cim:IdentifiedObject.mRID>Switch_295854688</cim:IdentifiedObject.mRID>
<cim:IdentifiedObject.aliasName>Switch_295854688</cim:IdentifiedObject.aliasName>
<cim:ConductingEquipment.phases
rdf:resource="http://iec.ch/TC57/2009/CIM-schema-cim14#PhaseCode.ABC" />
<cim:Switch.circuit2>0001406</cim:Switch.circuit2>
<cim:Equipment.Line rdf:resource="#Line_0001406" />
You could iterate through the RDF statements and populate your JAXB beans via a Bean population utility like BeanUtils.
Iterate the statements in such a form that statements with the same subject are processed in a group. The rdf:type statements define which Class to instantiate and the rest can be probably mapped to properties of the created beans.
If you are familiar with Java reflection then this is probably quite straightforward.
What isn't clear from your post is any mapping between the XSD components and the particular resource data you have in RDF (or schema thereof, such as RDFS or OWL, or both).
If you understand this mapping, then given you have a JAXB implementation to create Java objects already (with a view to populate them with the data represented as RDF) and a Jena implementation to parse the RDF/XML in Java, then I suggest that you can implement a Java 'bridge' - effectively custom code that queries the Jena model of the RDF data to map it into new objects of the classes as generated by JAXB, which can then be marshaled to the required XML.
If you'd rather not write any Java code at all to do this, you could write some XSLT or XQuery to transform your RDF/XML directly into the required XML, but this sounds like it will be more work than the aforementioned option given what you've got already.
Do the Resource/Subject/etc objects not have any methods for converting to DOM Element?
Alternatively (not the neatest solution for sure) what about serializing to string and reading the string then using the JAXB-created (from XSD) objects' setter methods??
Related
I implemented an xsd scanner, which creates an targetNamespace=<file.xsd> catalog.
Includes are filtered, so the catalog has only the root files of the targetNamespace.
With this catalog I'm resolving the required files (using a LSResourceResolver) to validate incoming xml files.
Map
namespace1=path/xsdForNameSpace1
namespace2=path/xsdForNameSpace2
:
But now I got multiple XSD, containing different content, but implementing the same targetNamespace.
Imho this is not correct, one namespace one root xsd - done
Example
schema1.xsd:
<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns="http://www.xxxxxxx.com/texxxxxxx"
targetNamespace="http://www.xxxxxxx.com/texxxxxxx"
elementFormDefault="qualified">
<xsd:include schemaLocation="xxxxxx_xxxxxx_xxxxx_xxxxx.xsd"/>
<xsd:element name="ab120">
<xsd:complexType>
:
schema2.xsd:
<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns="http://www.xxxxxxx.com/texxxxxxx"
targetNamespace="http://www.xxxxxxx.com/texxxxxxx"
elementFormDefault="qualified">
<xsd:include schemaLocation="xxxxxx_xxxxxx_xxxxx_xxxxx.xsd"/>
<xsd:element name="ab122">
<xsd:complexType>
:
I have two xml files are implementing the identical namespace http://www.xxxxxxx.com/texxxxxxx one with a root element ab120 the other with a root element ab122.
In this case my map contains only one of the implementing xsd files and I've no idea how to resolve the correct xsd for the incoming xml.
The incoming xml files look like this.
file1.xml:
<ab120 xmlns="http://www.xxxxxxx.com/texxxxxxx" ...>
:
</ab120>
file2.xml
<ab122 xmlns="http://www.xxxxxxx.com/texxxxxxx" ...>
:
</ab122>
The LSResourceResolver interface does not give me access to the xml, so I can't decide according the root node, which xsd I should use.
My temporary solution:
I added a second index with (namespace,xsd_file_name) that resolves correctly when the xml provides the implementing file (systemID)
targenNamespace="namespace myfile.xsd"
My question is, is it correct to specifiy multiple XSD file implementing the same namespace with different xsd structures ?
Edit:
It seemed to be not clear enough. Added two examples
My question is, is it correct to specifiy multiple XSD file implementing the same namespace with different xsd structures ?
Yes, that is a valid use of XML schema. A schema does not have to be represented by a single XSD file. Please see https://www.w3.org/TR/xmlschema-0/#SchemaInMultDocs and https://www.w3.org/TR/xmlschema-0/#import
You may also find this thread helpful: What's the difference between xsd:include and xsd:import?
Ok after asking w3c there is nothing in the specs that precludes this.
Reusing a targetNamespace with different content is allowed.
However, how to handle this, if you have to validate XMLs, is on your own and depends on the situation.
Possible solutions could be adding a version tag to the xml header or combine schemas if possible.
In my context nothing of the above would help, the resolver interface does not allow additional information, and the xsds can not be combined by a choice.
The only way to solve the issue is creating different index, resolver combinations. When creating the validator I have to use the correct resolver according to the origin where the xml came from.
How about including the two existing XSDs in a third one and use this for validation?
schema3.xsd:
<?xml version="1.0" encoding="utf-8" ?>
<xs:schema targetNamespace="http://www.xxxxxxx.com/texxxxxxx"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
elementFormDefault="qualified">
<xs:include schemaLocation="schema1.xsd"/>
<xs:include schemaLocation="schema2.xsd"/>
</xs:schema>
I have an application doing XML<->conversions using Jaxb and automatically generated classes with maven-jaxb2-plugin.
Someplace deep in my schema, I have the possibility to enter "ANY" xml.
Update: this better describes my schema. Some known XML wrapping a totally unknown part (the "any" part).
<xs:complexType name="MessageType">
<xs:sequence>
<xs:element name="XmlAnyPayload" minOccurs="0">
<xs:complexType>
<xs:sequence>
<xs:any namespace="##any"/>
</xs:sequence>
</xs:complexType>
</xs:element>
<xs:element name="OtherElements">
....
</xs:sequence>
This maps (by jaxb) to a inner class like this.
#XmlAccessorType(XmlAccessType.FIELD)
#XmlType(name = "", propOrder = {
"any"
})
public static class XmlAnyPayload {
#XmlAnyElement(lax = true)
protected Object any;
When I unmarshall the entire structure, it is no problem. The "Object any" will render into a org.apache.xerces.dom.ElementNSImpl. Now, I want to recreate the Java object manually and then go to XML. How do I take some random XML and put into the any (org.apache.xerces.dom.ElementNSImpl) element to be able to build up the Java object?
Also, the next case is when I have this element as java, I want to unmarshall this very part (to be able to extract the XML string of this element). But this is not possible. I get an exception about root elements. But it is not possible to annotate ElementNSImpl.
unable to marshal type "com.sun.org.apache.xerces.internal.dom.ElementNSImpl" as an element because it is missing an #XmlRootElement annotation
Do you have any suggestions on how to handle these problems?
#XmlAnyElement(lax = true) means in plain English something like:
Dear JAXB! If you have a mapping for this element, please unmarshal it
into a Java object. If you don't know this element, just leave it as a
DOM element.
This is exactly what is happening in your case. So if you want to actually unmarshal the content of this lax any, provide JAXB context with a mapping for the element you wish to unmarshal. The easiest way to do this is to annotate your class with #XmlRootElement
#XmlRootElement(name="foo", namespace="urn:bar")
public class MyClass { ... }
Now when you create your JAXB context, add MyClass into it:
JAXBContext context = JAXBContext.newInstance(A.class, B.class, ..., MyClass.class);
In this case, if JAXB meets the {urn:bar}foo element in the place of that xs:any, it will know that this element is mapped onto MyClass and will try to unmarshal MyClass.
If you are creating JAXB context based on the package name (you probably do), you can still add you class (say, com.acme.foo.MyClass) to it. The easiest way is to create a com/acme/foo/jaxb.index resource:
com.acme.foo.MyClass
And the add your package name to the context path:
JAXBContext context = JAXBContext.newInstance("org.dar.gee.schema:com.acme.foo");
There are other ways with ObjectFactory etc., but the trick with jaxb.index is probably the easiest one.
Alternatively, instead of unmarshalling everything in one run, you can leave the content of xs:any as DOM and unmarshal it into the target object in a second unmarshalling with anothe JAXB context (which know your MyClass class). Something like:
JAXBContext payloadContext = JAXBContext.newInstance(MyClass.class);
payloadContext.createUnmarshaller().unmarshal((Node) myPayload.getAny());
This approach is sometimes better, especially when you have a combination of container/payload schemas which are relatively independent. Depends on the case.
All said above applies to marshalling as well. It's all neatly bidirectional.
I think you need the XSDs for this "any" part and generate classes for them as well.
Here is some more information:
http://jaxb.java.net/guide/Mapping_of__xs_any___.html
Edit: if your object you want to marshal doesn't have the #XmlRootElement annotation (see error message), then I think you have to wrap it with a JAXBElement.
<xs:any/>
requires some not intuitive stuff to be converted to java object. If you have no difference, try using
<element name="any" type="xs:anyType"/>
I am using JAXB, I have xsd files which I cannot modify which has elements with
minOccurs="0" nillable="true"
Which results that in the generated ObjectFactory class I have a lot of code like:
JAXBElement<SomeObject>
Then the code, that I have to write to both marshall and unmarshall is boilerplate and ugly.
Is there a way to generate somehow automaticaly another layer of abstraction so that I would have some other ObjectFactory class as well, but it would not operate on JAXBElement but instead would have it wrapped in some methods? Like it is when in the binding.xjb you specify the following:
<jaxb:globalBindings localScoping="toplevel" generateElementProperty="false">
which is unfortunately not an option for me.
Normally there is a standard way how to validate XML against XSD schema in Java, but I need to have more sophisticated validation, for example validation for Zip Postal Code which ensure that such ZIP really exists. Therefore I need some kind of customizable Validator to which I will define rule like: (sorry for triviality)
if( validationType.equals("ZipPostalCode") {
com.fuu.validations.Address.zipPostalCode( innerText );
}
What is the most straightforward way to achieve such custom validations in Java with XSD and some customisable validator library?
If you use Saxon as your schema processor, then you can use XSD 1.1 assertions that invoke external Java methods:
<xs:simpleType name="zipCode" base="xs:string">
<xs:assertion test="ext:isValidZipCode($value)" xmlns:ext="java:com.fuu.validations.Address"/>
</xs:simpleType>
I have an XML content without defined attributes, like this:
<rootElement>
<subElement1/>
</rootElement>
I want to populate this XML content with required attributes defined in XML Schema (XSD) for this XML.
For example, according to XSD subElement1 has required attribute 'id'.
What is the best way (for Java processing) to detect that and add such attributes to XML?
We need to add required attributes and set appropriate values for them.
As a result for example above we need to have the following XML:
<rootElement>
<subElement1 id="some-value"/>
</rootElement>
In the XML schema definition, i.e. XSD file, attributes are optional by default. To make an attribute required, you have to define:
<xs:attribute name="surname" type="xs:string" use="required"/>
You will find a very good introduction on XML and XML Schema Definitions, i.e. XSD, on W3 Schools.
In Java the equivalent of defining a XML schema is using JAXB, i.e. Java API for XML Binding that is included into Java SE. There you would define, e.g.
#XmlRootElement
public class Person { public #XmlAttribute(required=true) String surname; }
Hope this could clarify your question.
I would suggest you to use JAXB for that. Search the Internet for tutorials.
Steps to proceed further with JAXB,
Generate Java files using JAXB by providing the schema
Unmarshal your XML to generated Java classes (beans). Don't do validation or set validation handler here.
Populate those classes with appropriate values. required elements can be found using annotation look up. JAXB annotation for element would look like something, #XmlElement(name = "ElementName", required = true). And an attribute annotation would be something similar to this, #XmlAttribute(required = true)
Marshal your bean back to XML. You can validate your bean using ValidationHandler, while marshalling. Below is the sample code snippet,
marshller = JAXBContext.newInstance(pkgOrClassName).createUnmarshaller();
marshller.setSchema(getSchema(xsd)); // skip this line for unmarshaller
marshller.setEventHandler(new ValidationHandler()); // skip this line for unmarshaller
Use a DOM parser.Has methods to traverse XML trees, access, insert, and delete nodes
I have had the same idea of Cris but I think that with this validator you don't have information about the point in which you have had the error.
I think that you have to create or extend your own validator.