Jesse Brands Jesse Brands - 27 days ago 11
Java Question

Invalid content was found starting with element 'Element'. One of 'Element' is expected

I'm using Java's JAXB parser to marshal and unmarshal XML files to a specific class. The unmarshaling works fine, however, it chokes on marshaling a file with the following error:


cvc-complex-type.2.4.a: Invalid content was found starting with element 'DataPath'. One of '{"http://hg.flyingwhales.nl/cinnabar/schemas/project.xsd":DataPath}' is expected.


I don't understand why this wouldn't work, cause unmarshaling a file works fine.

As requested, here is a MCVE:

ProjectRepository.java:

package com.flyingwhales;

import org.xml.sax.SAXException;

import javax.xml.XMLConstants;
import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBException;
import javax.xml.bind.Marshaller;
import javax.xml.bind.Unmarshaller;
import javax.xml.transform.Source;
import javax.xml.transform.stream.StreamSource;
import javax.xml.validation.Schema;
import javax.xml.validation.SchemaFactory;
import javax.xml.validation.Validator;
import java.io.File;
import java.io.IOException;

/**
* Created by Jesse on 11/13/2016.
*/

public final class ProjectRepository {
private static Validator validator;
private static Marshaller marshaller;
private static Unmarshaller unmarshaller;

// This class is not meant to be instantiated.
private ProjectRepository() {}

static {
File schemaFile = new File("project.xsd");
SchemaFactory schemaFactory = SchemaFactory
.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI);

try {
Schema schema = schemaFactory.newSchema(schemaFile);
validator = schema.newValidator();

// Initialize JAXB stuff too.
JAXBContext jaxbContext = JAXBContext.newInstance(Project.class);
marshaller = jaxbContext.createMarshaller();
marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
marshaller.setSchema(schema);
unmarshaller = jaxbContext.createUnmarshaller();
} catch (SAXException | JAXBException e) {
e.printStackTrace();
}
}

public static Project load(File file) throws Exception {
if (!validate(file)) {
throw new Exception("project file contains invalid markup");
}

Project project = (Project)unmarshaller.unmarshal(file);
project.setProjectFile(file);

return project;
}

public static void save(Project project, File path) throws JAXBException {
marshaller.marshal(project, path);
}

public static boolean validate(File file) throws IOException {
Source source = new StreamSource(file);

try {
validator.validate(source);
} catch (SAXException e) {
return false;
}

return true;
}
}


Project.java

package com.flyingwhales;

import javax.xml.bind.annotation.XmlAttribute;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlRootElement;
import java.io.File;

@XmlRootElement(name = "Project")
public class Project {
private String name = "Project";
private String type = "Standalone";
private String dataPath = "Data";
private String scriptPath = "Data/Scripts";
private String artPath = "Art";
private String sfxPath = "Sound/SFX";
private String bgmPath = "Sound/BGM";

private File projectFile;

public Project() { }

public Project(File file) {
setProjectFile(file);
}

@XmlAttribute( name = "name" )
public String getName() {
return name;
}

public void setName(String name) {
this.name = name;
}

@XmlAttribute (name = "type" )
public String getType() {
return type;
}

public void setType(String type) {
this.type = type;
}

@XmlElement(name = "DataPath")
public String getDataPath() {
return dataPath;
}

public void setDataPath(String dataPath) {
this.dataPath = dataPath;
}

@XmlElement (name = "ScriptPath")
public String getScriptPath() {
return scriptPath;
}

public void setScriptPath(String scriptPath) {
this.scriptPath = scriptPath;
}

@XmlElement (name = "ArtPath")
public String getArtPath() {
return artPath;
}

public void setArtPath(String artPath) {
this.artPath = artPath;
}

@XmlElement (name = "SFXPath")
public String getSfxPath() {
return sfxPath;
}

public void setSfxPath(String sfxPath) {
this.sfxPath = sfxPath;
}

@XmlElement (name = "BGMPath")
public String getBgmPath() {
return bgmPath;
}

public void setBgmPath(String bgmPath) {
this.bgmPath = bgmPath;
}

void setProjectFile(File file) {
projectFile = file;
}

public File getProjectFile() {
return projectFile;
}
}


package-info.java

@XmlSchema(namespace = "http://hg.flyingwhales.nl/cinnabar/schemas/project.xsd")
package com.flyingwhales;

import javax.xml.bind.annotation.XmlSchema;


Main.java

package com.flyingwhales;

import javax.xml.bind.JAXBException;
import java.io.File;

public class Main {
public static void main(String[] args) {
// Open and parse a file from the filesystem.
try {
Project loadedProject = ProjectRepository.load(new File("Test.cinnabar"));
System.out.print("Loaded project: " + loadedProject.getName());
} catch (Exception e) {
e.printStackTrace();
}

// Try to create a project and save it to the hard drive.
Project newProject = new Project(new File("New.cinnabar"));

try {
ProjectRepository.save(newProject, new File("New.cinnabar"));
} catch (JAXBException e) {
e.printStackTrace();
}
}
}


project.xsd

<?xml version="1.0" encoding="UTF-8" ?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"
targetNamespace="http://hg.flyingwhales.nl/cinnabar/schemas/project.xsd"
xmlns="http://hg.flyingwhales.nl/cinnabar/schemas/project.xsd"
elementFormDefault="qualified">

<xs:element name="Project">
<xs:complexType>
<xs:sequence>
<xs:element name="DataPath" type="xs:string" />
<xs:element name="ScriptPath" type="xs:string" />
<xs:element name="ArtPath" type="xs:string" />
<xs:element name="SFXPath" type="xs:string" />
<xs:element name="BGMPath" type="xs:string" />
</xs:sequence>

<xs:attribute name="name" type="xs:string" use="required" />
<xs:attribute name="type" type="xs:string" use="required" />
</xs:complexType>
</xs:element>
</xs:schema>


Test.cinnabar

<?xml version="1.0" encoding="UTF-8"?>
<Project name="Test project" type="Standalone"
xmlns="http://hg.flyingwhales.nl/cinnabar/schemas/project.xsd"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://hg.flyingwhales.nl/cinnabar/schemas/project.xsd">

<DataPath>Data</DataPath>
<ScriptPath>Data/Scripts</ScriptPath>
<ArtPath>Art</ArtPath>
<SFXPath>Sound/SFX</SFXPath>
<BGMPath>Sound/BGM</BGMPath>
</Project>


Simply run the code and it will produce the error. Make sure project.xsd is in the working directory.

Answer

Two things:

  • Your package-info.java is missing elementFormDefault = javax.xml.bind.annotation.XmlNsForm.QUALIFIED
  • As @sahoora correctly noticed, you also need propOrder for the correct ordering of elements when marshalling.

package-info.java:

@XmlSchema(namespace = "http://hg.flyingwhales.nl/cinnabar/schemas/project.xsd", elementFormDefault = javax.xml.bind.annotation.XmlNsForm.QUALIFIED)
package com.flyingwhales;

import javax.xml.bind.annotation.XmlSchema;

Project.java:

@XmlRootElement(name = "Project")
@XmlType(name = "", propOrder = {
        "dataPath",
        "scriptPath",
        "artPath",
        "sfxPath",
        "bgmPath"
    })
public class Project { ... }

The core of the problem was missing elementFormDefault=QUALIFIED. This lead for local elements to be marshalled in empty namespace therefore schema validation and subsequent unmarshalling failed.