XMLDecoder >> java.lang.IllegalArgumentException: Unsupported element - java

Got this error
java.lang.IllegalArgumentException: Unsupported element: net
from this example xml file
<?xml version="1.0" encoding="UTF-8"?>
<net>
<node label="A">
...
</node>
<node label="B">
...
</node>
<node label="C">
...
</node>
</net>
with these java code lines
...
FileInputStream file = new FileInputStream("example.xml");
XMLDecoder decoder = new XMLDecoder(file);
Object decodedResistors = (Object) decoder.readObject();
file.close();
...

Do not use java.beans.XMLDecoder for deserialisation custom XML payloads. It was not designed for that. Read article Long Term Persistence of JavaBeans Components: XML Schema. It contains some example XML payloads which can be deserialised back by XMLDecoder:
<?xml version="1.0" encoding="UTF-8" ?>
<java version="1.4.0" class="java.beans.XMLDecoder">
<void id="myController" property="owner"/>
<object class="javax.swing.JButton">
<void method="addActionListener">
<object class="java.beans.EventHandler" method="create">
<class>java.awt.event.ActionListener</class>
<object idref="myController"/>
<string>doIt</string>
</object>
</void>
</object>
</java>
If you need to deserialise custom XML use JAXB or Jackson XML. You need to create a POJO model with JAXB annotations:
#XmlRootElement(name = "net")
#XmlAccessorType(XmlAccessType.FIELD)
class Net {
#XmlElement(name = "node")
private List<Node> nodes;
// getters, setters, toString
}
#XmlAccessorType(XmlAccessType.FIELD)
class Node {
#XmlAttribute
private String label;
// getters, setters, toString
}
Example usage:
import javax.xml.bind.JAXBContext;
import javax.xml.bind.Unmarshaller;
import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlAttribute;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlRootElement;
import java.io.File;
import java.util.List;
public class JaxbApp {
public static void main(String[] args) throws Exception {
File xmlFile = new File("./resource/test.xml").getAbsoluteFile();
JAXBContext jaxbContext = JAXBContext.newInstance(Net.class);
Unmarshaller unmarshaller = jaxbContext.createUnmarshaller();
Object net = unmarshaller.unmarshal(xmlFile);
System.out.println(net);
}
}
prints:
Net{nodes=[Node{label='A'}, Node{label='B'}, Node{label='C'}]}
See also:
java.lang.IllegalArgumentException: Unsupported element: rss

Related

How to marshall XML with namespace in JAXB?

I would like to marshal XML using defined XSD schema:
<?xml version="1.0" encoding="UTF-8"?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"
targetNamespace="myNamespace" xmlns="myNamespace">
<xs:element name="element" type="JavaBean" />
<xs:complexType name="JavaBean">
</xs:complexType>
</xs:schema>
This is empty Java bean type:
package main;
import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlType;
#XmlAccessorType(XmlAccessType.FIELD)
#XmlType(name = "JavaBean")
public class JavaBean {
}
Simple test results in "Cannot find the declaration of element 'element'." error.
package main;
import java.io.File;
import java.io.IOException;
import javax.xml.XMLConstants;
import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBElement;
import javax.xml.bind.JAXBException;
import javax.xml.bind.Marshaller;
import javax.xml.namespace.QName;
import javax.xml.validation.Schema;
import javax.xml.validation.SchemaFactory;
import org.xml.sax.SAXException;
public class Test {
public static void main(String[] args) throws JAXBException, IOException, SAXException {
final SchemaFactory factory = SchemaFactory.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI);
Schema schema = factory.newSchema(new File("sample.xsd"));
JavaBean message = new JavaBean();
JAXBElement<JavaBean> element = new JAXBElement<>(new QName("element"), JavaBean.class, message);
JAXBContext context = JAXBContext.newInstance(JavaBean.class);
Marshaller jaxbMarshaller = context.createMarshaller();
jaxbMarshaller.setSchema(schema);
jaxbMarshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
jaxbMarshaller.marshal(element, System.out);
}
}
What is interesting, when I remove targetNamespace and xmlns from XML Schema Definition, it works good.
What am I doing wrong?
According to your schema, the element is bound to the myNamespace namespace by default.
Thus you need to create JavaBean with the proper namespace:
JAXBElement<JavaBean> element = new JAXBElement<>(new QName("myNamespace", "element"), JavaBean.class, message);

JAXB getting Object "" is found in an IDREF property but this object doesnt have an ID

Here is a test case using a file from SCORM for imsmanifest,xml. This XML has been in use for about 5 or more years, and being a standard I don't want to change it unless required to get this to work.
You can find the xsd file here
The error occurs between <organizations default="CYBER4.ORG"> and <organization identifier="CYBER4.ORG">
In my project, this is the entry from my pom.xml for my jaxb version
<dependency>
<groupId>javax.xml.bind</groupId>
<artifactId>jaxb-api</artifactId>
<version>2.2.11</version>
</dependency>
to generate the Java code I ran (this is the install of xjc for Ubuntu 14.04)
$ xjc -version
xjc 2.2.4-2
$ xjc -verbose -p org.cyber4.scorm2004.xml.manifest.imscp imscp_v1p2.xsd
The output generates (amongst other things)
public class OrganizationsType {
#XmlAttribute(name = "default")
#XmlIDREF
#XmlSchemaType(name = "IDREF")
protected Object _default;
}
and
public class OrganizationType {
#XmlAttribute(name = "identifier", required = true)
#XmlJavaTypeAdapter(CollapsedStringAdapter.class)
#XmlID
#XmlSchemaType(name = "ID")
protected String identifier;
}
This is the test code
package org.cyber4.scorm2004.build;
import javax.xml.bind.JAXBContext;
import javax.xml.bind.Marshaller;
import org.cyber4.scorm2004.xml.manifest.imscp.ManifestMetadataType;
import org.cyber4.scorm2004.xml.manifest.imscp.ManifestType;
import org.cyber4.scorm2004.xml.manifest.imscp.ObjectFactory;
import org.cyber4.scorm2004.xml.manifest.imscp.OrganizationType;
import org.cyber4.scorm2004.xml.manifest.imscp.OrganizationsType;
import org.cyber4.scorm2004.xml.manifest.imscp.ResourcesType;
//import org.slf4j.Logger;
//import org.slf4j.LoggerFactory;
public class TestSCORMBuilder {
public static void main(String args[]) {
try {
ObjectFactory objectFactory = new ObjectFactory();
/*
* <metadata />
*/
ManifestMetadataType metadataType = objectFactory.createManifestMetadataType();
/*
* <organizations default="CYBER4.ORG">
* <organization identifier="CYBER4.ORG" />
* </organizations>
*/
// https://java.net/jira/browse/JAXB-872
OrganizationsType organizationsType = objectFactory.createOrganizationsType();
organizationsType.setDefault("CYBER4.ORG");
OrganizationType organizationType = objectFactory.createOrganizationType();
organizationType.setIdentifier("CYBER4.ORG");
organizationsType.getOrganization().add(organizationType);
/*
* <resources />
*/
ResourcesType resourcesType = objectFactory.createResourcesType();
/*
* <manifest>
* <metadata/ >
* <organizations default="CYBER4.ORG">
* <organization identifier="CYBER4.ORG" />
* </organizations>
* <resources />
* <manifest>
*/
ManifestType manifestType = objectFactory.createManifestType();
manifestType.setMetadata(metadataType);
manifestType.setOrganizations(organizationsType);
manifestType.setResources(resourcesType);
JAXBContext context = JAXBContext.newInstance("org.cyber4.scorm2004.xml.manifest.imscp");
Marshaller marshaller = context.createMarshaller();
marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
marshaller.setProperty(Marshaller.JAXB_SCHEMA_LOCATION,
"http://www.imsglobal.org/xsd/imscp_v1p1 imscp_v1p1.xsd");
marshaller.marshal(objectFactory.createManifest(manifestType), System.out);
} catch (Exception exception) {
exception.printStackTrace();
}
}
}
When I run the code I get this error
javax.xml.bind.MarshalException
- with linked exception:
[com.sun.istack.internal.SAXException2: Object "CYBER4.ORG" is found in an IDREF property but this object doesnt have an ID.]
at com.sun.xml.internal.bind.v2.runtime.MarshallerImpl.write(MarshallerImpl.java:311)
at com.sun.xml.internal.bind.v2.runtime.MarshallerImpl.marshal(MarshallerImpl.java:236)
at javax.xml.bind.helpers.AbstractMarshallerImpl.marshal(AbstractMarshallerImpl.java:95)
at org.cyber4.scorm2004.build.TestSCORMBuilder.main(TestSCORMBuilder.java:73)
Caused by: com.sun.istack.internal.SAXException2: Object "CYBER4.ORG" is found in an IDREF property but this object doesnt have an ID.
at com.sun.xml.internal.bind.v2.runtime.XMLSerializer.reportError(XMLSerializer.java:237)
at com.sun.xml.internal.bind.v2.runtime.XMLSerializer.errorMissingId(XMLSerializer.java:1045)
at com.sun.xml.internal.bind.v2.runtime.reflect.TransducedAccessor$IDREFTransducedAccessorImpl.print(TransducedAccessor.java:275)
at com.sun.xml.internal.bind.v2.runtime.reflect.TransducedAccessor$IDREFTransducedAccessorImpl.print(TransducedAccessor.java:254)
at com.sun.xml.internal.bind.v2.runtime.property.AttributeProperty.serializeAttributes(AttributeProperty.java:86)
at com.sun.xml.internal.bind.v2.runtime.ClassBeanInfoImpl.serializeAttributes(ClassBeanInfoImpl.java:360)
at com.sun.xml.internal.bind.v2.runtime.XMLSerializer.childAsXsiType(XMLSerializer.java:678)
at com.sun.xml.internal.bind.v2.runtime.property.SingleElementNodeProperty.serializeBody(SingleElementNodeProperty.java:143)
at com.sun.xml.internal.bind.v2.runtime.ClassBeanInfoImpl.serializeBody(ClassBeanInfoImpl.java:343)
at com.sun.xml.internal.bind.v2.runtime.XMLSerializer.childAsXsiType(XMLSerializer.java:685)
at com.sun.xml.internal.bind.v2.runtime.property.SingleElementNodeProperty.serializeBody(SingleElementNodeProperty.java:143)
at com.sun.xml.internal.bind.v2.runtime.ElementBeanInfoImpl$1.serializeBody(ElementBeanInfoImpl.java:145)
at com.sun.xml.internal.bind.v2.runtime.ElementBeanInfoImpl$1.serializeBody(ElementBeanInfoImpl.java:115)
at com.sun.xml.internal.bind.v2.runtime.ElementBeanInfoImpl.serializeBody(ElementBeanInfoImpl.java:317)
at com.sun.xml.internal.bind.v2.runtime.ElementBeanInfoImpl.serializeRoot(ElementBeanInfoImpl.java:324)
at com.sun.xml.internal.bind.v2.runtime.ElementBeanInfoImpl.serializeRoot(ElementBeanInfoImpl.java:60)
at com.sun.xml.internal.bind.v2.runtime.XMLSerializer.childAsRoot(XMLSerializer.java:483)
at com.sun.xml.internal.bind.v2.runtime.MarshallerImpl.write(MarshallerImpl.java:308)
... 3 more
If I comment out line 37
organizationsType.setDefault("CYBER4.ORG");
it generates this XML
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<manifest xmlns="http://www.imsglobal.org/xsd/imscp_v1p1" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.imsglobal.org/xsd/imscp_v1p1 imscp_v1p1.xsd">
<metadata/>
<organizations>
<organization identifier="CYBER4.ORG"/>
</organizations>
<resources/>
</manifest>
but it's missing the
default="CYBER4.ORG"
in <organizations> which is required for imsmanifest.xml to be valid.
This looks like this bug but I want to be sure I haven't missed anything.
default should not contain the id of the object you want to reference, but an object with the same id than the object you want to reference.
Try to replace :
organizationsType.setDefault("CYBER4.ORG");
with :
OrganizationType o = new OrganizationType()
o.setIdentifier("CYBER4.ORG");
organizationsType.setDefault(o);
If organizationType has already been set you can maybe also try :
organizationsType.setDefault(organizationType);

Prevent XXE Attack with JAXB

Recently, we had a security audit on our code, and one of the problem is that our application is subject to the Xml eXternal Entity (XXE) attack.
Basically, the application is a calculator that receives inputs as XML, through a Web-Service.
Here is an example of such an XXE attack on our application:
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/">
<soapenv:Header/>
<soapenv:Body>
<foo:calculateStuff>
<!--Optional:-->
<xmlInput><![CDATA[<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<!DOCTYPE currency [
<!ENTITY include SYSTEM "file:///d:/" >]>
<calcinput>...</calcinput>
]]></xmlInput>
</foo:calculateStuff>
</soapenv:Body>
</soapenv:Envelope>
As you can see, we can refer to an entity that points to an external file ("file:///d:/").
Regarding the XML input itself (the <calcinput>...</calcinput> part) is unmarshalled with JAXB (v2.1). The web-service part is based on jaxws-rt (2.1).
What do I need to do to secure my web-service?
JAXB
You can prevent the Xml eXternal Entity (XXE) attack by unmarshalling from an XMLStreamReader that has the IS_SUPPORTING_EXTERNAL_ENTITIES and/or XMLInputFactory.SUPPORT_DTD properties set to false.
JAX-WS
A JAX-WS implementation should take care of this for you. If it doesn't I would recommend opening a bug against the specific implmententation.
EXAMPLE
Demo
package xxe;
import javax.xml.bind.*;
import javax.xml.stream.*;
import javax.xml.transform.stream.StreamSource;
public class Demo {
public static void main(String[] args) throws Exception {
JAXBContext jc = JAXBContext.newInstance(Customer.class);
XMLInputFactory xif = XMLInputFactory.newFactory();
xif.setProperty(XMLInputFactory.IS_SUPPORTING_EXTERNAL_ENTITIES, false);
xif.setProperty(XMLInputFactory.SUPPORT_DTD, false);
XMLStreamReader xsr = xif.createXMLStreamReader(new StreamSource("src/xxe/input.xml"));
Unmarshaller unmarshaller = jc.createUnmarshaller();
Customer customer = (Customer) unmarshaller.unmarshal(xsr);
Marshaller marshaller = jc.createMarshaller();
marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
marshaller.marshal(customer, System.out);
}
}
input.xml
This XML document contains an entity that has been setup to get the listing of files I used to create this example.
<?xml version="1.0"?>
<!DOCTYPE customer
[
<!ENTITY name SYSTEM "/Users/bdoughan/Examples/src/xxe/">
]
>
<customer>
<name>&name;</name>
</customer>
Customer
package xxe;
import javax.xml.bind.annotation.XmlRootElement;
#XmlRootElement
public class Customer {
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
Output - Default Configuration
By default the entity will be resolved.
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<customer>
<name>Customer.java
Demo.java
input.xml
</name>
</customer>
Output when XMLInputFactory.IS_SUPPORTING_EXTERNAL_ENTITIES property is set to false
When this property is set the entity is not resolved.
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<customer>
<name></name>
</customer>
Output when XMLInputFactory.SUPPORT_DTD property is set to false
When this property is set an exception is thrown trying to resolve the entity.
Exception in thread "main" javax.xml.bind.UnmarshalException
- with linked exception:
[javax.xml.stream.XMLStreamException: ParseError at [row,col]:[8,15]
Message: The entity "name" was referenced, but not declared.]
at com.sun.xml.bind.v2.runtime.unmarshaller.UnmarshallerImpl.handleStreamException(UnmarshallerImpl.java:436)
at com.sun.xml.bind.v2.runtime.unmarshaller.UnmarshallerImpl.unmarshal0(UnmarshallerImpl.java:372)
at com.sun.xml.bind.v2.runtime.unmarshaller.UnmarshallerImpl.unmarshal(UnmarshallerImpl.java:342)
at xxe.Demo.main(Demo.java:18)
Caused by: javax.xml.stream.XMLStreamException: ParseError at [row,col]:[8,15]
Message: The entity "name" was referenced, but not declared.
at com.sun.org.apache.xerces.internal.impl.XMLStreamReaderImpl.next(XMLStreamReaderImpl.java:598)
at com.sun.xml.bind.v2.runtime.unmarshaller.StAXStreamConnector.bridge(StAXStreamConnector.java:196)
at com.sun.xml.bind.v2.runtime.unmarshaller.UnmarshallerImpl.unmarshal0(UnmarshallerImpl.java:370)
... 2 more

With MOXy and XPath, is it possible to unmarshal a list of attributes?

Edit: here's how I'm loading the XML document, as I used it in Blaise's answer. I'm loading it like this because I want to work with a node, not the whole doc. Even using the whole document I'm still having trouble when loading in this manner.
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
factory.setValidating(false);
factory.setNamespaceAware(false);
DocumentBuilder builder = factory.newDocumentBuilder();
Document doc = builder.parse("[path to doc]/input.xml");
TestClass testClass = (TestClass) unmarshaller.unmarshal(doc);
I've got XML that looks like this:
<test>
<items>
<item type="cookie">cookie</item>
<item type="crackers">crackers</item>
</items>
</test>
And a class:
#XmlAccessorType(XmlAccessType.FIELD)
#XmlRootElement(name = "test")
public class TestClass
{
#XmlPath("items/item/text()")
#XmlElement
private ArrayList<String> itemList = new ArrayList<String>();
// getters, setters omitted
}
The above code will work whether or not I have #XmlElement, and I get an ArrayList containing [cookie, crackers].
If I change the declaration above to
#XmlPath("items/item/#type")
#XmlElement
private ArrayList<String> itemList = new ArrayList<String>();
my ArrayList is empty.
My ultimate goal is to just have attributes so my XML would look like this:
<test>
<items>
<item type="cookie"/>
<item type="crackers"/>
</items>
</test>
Is what I'm trying to do, pull out a list of attributes using XPath, possible, and if so, how?
Thank you.
UPDATE
I have been able to confirm the issue you are seeing (https://bugs.eclipse.org/353763). A fix has been added into our EclipseLink 2.3.1 and 2.4.0 streams and can be obtained from the nightly download page starting August 4th, 2011:
http://www.eclipse.org/eclipselink/downloads/nightly.php
Workaround:
You can workaround this issue by setting your DocumentBuilderFactory to be namespace aware:
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
factory.setNamespaceAware(true);
DocumentBuilder builder = factory.newDocumentBuilder();
Document doc = builder.parse("src/forum6907225/input.xml");
testClass = (TestClass) unmarshaller.unmarshal(doc);
marshaller.marshal(testClass, System.out);
You are doing the mapping correctly (see below). Have you included a jaxb.properties file to specify EclipseLink MOXy as your JAXB provider?:
http://blog.bdoughan.com/2011/05/specifying-eclipselink-moxy-as-your.html
Test Class
package forum6907225;
import java.util.ArrayList;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlRootElement;
import org.eclipse.persistence.oxm.annotations.XmlPath;
#XmlAccessorType(XmlAccessType.FIELD)
#XmlRootElement(name = "test")
public class TestClass
{
#XmlPath("items/item/#type")
#XmlElement
private ArrayList<String> itemList = new ArrayList<String>();
// getters, setters omitted
}
Demo
package forum6907225;
import java.io.File;
import javax.xml.bind.JAXBContext;
import javax.xml.bind.Marshaller;
import javax.xml.bind.Unmarshaller;
import org.eclipse.persistence.Version;
public class Demo {
public static void main(String[] args) throws Exception {
JAXBContext jc = JAXBContext.newInstance(TestClass.class);
System.out.println(Version.getVersionString());
Unmarshaller unmarshaller = jc.createUnmarshaller();
File xml = new File("src/forum6907225/input.xml");
TestClass testClass = (TestClass) unmarshaller.unmarshal(xml);
Marshaller marshaller = jc.createMarshaller();
marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
marshaller.marshal(testClass, System.out);
}
}
input.xml
<?xml version="1.0" encoding="UTF-8"?>
<test>
<items>
<item type="cookie">cookie</item>
<item type="crackers">crackers</item>
</items>
</test>
Output
2.3.1.qualifier
<?xml version="1.0" encoding="UTF-8"?>
<test>
<items>
<item type="cookie"/>
<item type="crackers"/>
</items>
</test>

how to find in xsd, whether the particular tag is available in xml or not?

My xml file is,
<?xml version="1.0"?>
<type xmlns:xs="http://www.w3.org/2001/XMLSchema-instance"
xs:noNamespaceSchemaLocation="datatype.xsd">
<int>integer</int>
<varchar>varcharacter</varchar>
<double>doubles</double>
</type>
In this xml, I want to set <float></float> as mandatory. But i didn't use this tag. So how to validate the <float> is present or not in my xml file, using xsd with java.? Thanks in advance.
The following can be used to validate your XML against a schema:
import java.io.File;
import javax.xml.XMLConstants;
import javax.xml.validation.Schema;
import javax.xml.validation.SchemaFactory;
import javax.xml.validation.Validator;
public class Demo {
public static void main(String[] args) throws Exception {
SchemaFactory sf = SchemaFactory.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI);
Schema schema = sf.newSchema(new File("datatype.xsd"));
Validator validator = schema.newValidator();
validator.setErrorHandler(new MyErrorHandler());
validator.validate(source);
}
}
For a more detailed example see:
http://bdoughan.blogspot.com/2010/11/validate-jaxb-object-model-with-xml.html

Categories