I'm trying to do something which I thought was pretty simple, which is to use JSON for persisting a POJO container with various properties in it.
After reading up about various ways to marshal/unmarshal JSON without all the web services stuff on top, I figured MOXy would be the best way as it seems to be able to handle non-JAXB annotated POJOs. I'm using MOXy 2.7.3.
However, I ran into two issues:
I'm using Maven with the following dependency:
<dependency> <groupId>org.eclipse.persistence</groupId> <artifactId>org.eclipse.persistence.moxy</artifactId> <version>2.7.3</version> </dependency>
However, if I don't also include this dependency on a GlassFish JAR:
<dependency>
<groupId>org.glassfish</groupId>
<artifactId>javax.json</artifactId>
<version>1.1.2</version>
</dependency>
I get the following exception when attempting to unmarshal (marshaling works fine):
Caused by: Exception [EclipseLink-25004] (Eclipse Persistence Services - 2.7.3.v20180807-4be1041): org.eclipse.persistence.exceptions.XMLMarshalException
Exception Description: An error occurred unmarshalling the document
Internal Exception: javax.json.JsonException: Provider org.glassfish.json.JsonProviderImpl not found
at org.eclipse.persistence.exceptions.XMLMarshalException.unmarshalException(XMLMarshalException.java:122)
at org.eclipse.persistence.internal.oxm.record.json.JsonStructureReader.parse(JsonStructureReader.java:148)
...
Why does MOXy need the GlassFish JsonProviderImpl dependency?
I need to declare an
@XmlRootElement
in my container class. Not sure why, but this is the case even if I usemarshaller.setProperty(MarshallerProperties.JSON_INCLUDE_ROOT, true);
, which seems to be the default. If I don't don't have an@XmlRootElement
I get the following exception when trying to unmarshal (marshaling works fine):Caused by: Exception [EclipseLink-25008] (Eclipse Persistence Services - 2.7.3.v20180807-4be1041): org.eclipse.persistence.exceptions.XMLMarshalException Exception Description: A descriptor with default root element stringValue was not found in the project
Maybe I'm just confused, but it seemed like from the docs that MOXy was able to work with POJOs?
Here is some example code showing what I'm doing:
import java.io.File;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.Reader;
import java.io.Writer;
import java.nio.file.Paths;
import java.time.ZonedDateTime;
import javax.xml.bind.JAXBContext;
import javax.xml.bind.Marshaller;
import javax.xml.bind.Unmarshaller;
import org.eclipse.persistence.jaxb.JAXBContextFactory;
import org.eclipse.persistence.jaxb.MarshallerProperties;
public class MoxyTest
{
public static void main(String[] args) throws Exception
{
// Create an instance of the container to be written/read with JSON
TestContainer testContainer = new TestContainer();
testContainer.stringValue = "test string";
testContainer.intValue = 5;
testContainer.zonedDateTimeValue = ZonedDateTime.now().minusMonths(1);
// Note that JAXBContextFactory is specific to MOXy, this was done to avoid having to create a jaxb.properties
// See https://stackoverflow.com/questions/6963996/can-i-replace-jaxb-properties-with-code and
// https://stackoverflow.com/questions/28676613/set-moxy-as-jaxb-provider-without-properties-file-in-the-same-package
JAXBContext jaxbContext = JAXBContextFactory.createContext(new Class[] {TestContainer.class}, null);
Marshaller marshaller = jaxbContext.createMarshaller();
// By default the marshaller will output XML, we have to tell it to use JSON instead
marshaller.setProperty(MarshallerProperties.MEDIA_TYPE, "application/json");
File dataFile = Paths.get("/temp", "datafile.json").toFile();
Writer writer = new FileWriter(dataFile);
marshaller.marshal(testContainer, writer);
writer.close();
Unmarshaller unmarshaller = jaxbContext.createUnmarshaller();
// By default the unmarshaller will try to read XML, we have to tell it to use JSON instead
unmarshaller.setProperty(MarshallerProperties.MEDIA_TYPE, "application/json");
// Determine the location for the file which is used to persist the data
if (dataFile.exists() && (dataFile.length() > 0))
{
Reader reader = new FileReader(dataFile);
TestContainer readTestContainer = (TestContainer) unmarshaller.unmarshal(reader);
System.out.println(readTestContainer.intValue);
System.out.println(readTestContainer.stringValue);
System.out.println(readTestContainer.zonedDateTimeValue);
reader.close();
}
}
}
And the test container (with @XmlRootElement
):
import java.time.ZonedDateTime;
import javax.xml.bind.annotation.XmlRootElement;
@XmlRootElement(name = "testContainer")
public class TestContainer
{
public String stringValue;
public int intValue;
public ZonedDateTime zonedDateTimeValue;
}
Note that for extra awesomeness, maybe someone can answer why ZonedDateTime will properly marshal but not unmarshal (result is null
)? Here are the results from running MoxyTest
:
5
test string
null