Generate a XSD from a JAXB-annotated class without using File - java

I am trying to generate XSD from Java Annotated classes by following code mentioned in this post Is it possible to generate a XSD from a JAXB-annotated class
JAXBContext jaxbContext = JAXBContext.newInstance(Customer.class);
SchemaOutputResolver sor = new MySchemaOutputResolver();
jaxbContext.generateSchema(sor);
public class MySchemaOutputResolver extends SchemaOutputResolver {
public Result createOutput(String namespaceURI, String suggestedFileName) throws IOException {
File file = new File(suggestedFileName);
StreamResult result = new StreamResult(file);
result.setSystemId(file.toURI().toURL().toString());
return result;
}
}
This technique is using File system, My requirement is to get the XML as String without using file system.
Is there any possibility the Implementation of SchemaOutputResolver may not write file to disk and return or set some instance variable with the String value.

You can write the StreamResult on a StringWriter and get the string from that.
JAXBContext jaxbContext = JAXBContext.newInstance(Customer.class);
MySchemaOutputResolver sor = new MySchemaOutputResolver();
jaxbContext.generateSchema(sor);
String schema = sor.getSchema();
public class MySchemaOutputResolver extends SchemaOutputResolver {
private StringWriter stringWriter = new StringWriter();
public Result createOutput(String namespaceURI, String suggestedFileName) throws IOException {
StreamResult result = new StreamResult(stringWriter);
result.setSystemId(suggestedFileName);
return result;
}
public String getSchema() {
return stringWriter.toString();
}
}

Related

escape characters (e.g. quotes) using JAXB Marshaller

I need use a XmlAdapter because I want do not have empty XLM object:
<TACHES>
<TACHE>
<NB_HEURE>7.75</NB_HEURE>
<NOTES>foo</NOTES>
<CODE_TACHE>1234</CODE_TACHE>
</TACHE>
<TACHE> <- I Want delete this part
<NB_HEURE/> <- I Want delete this part
<NOTES/> <- I Want delete this part
<CODE_TACHE/> <- I Want delete this part
</TACHE> <- I Want delete this part
</TACHES>
My POJO is:
#XmlRootElement(name = "TACHES")
public class Tasks {
private List<Task> tasks;
#XmlJavaTypeAdapter(TaskAdapter.class)
#XmlElement(name = "TACHE")
public List<Task> getTasks() {
return tasks;
}
public void setTasks(List<Task> tasks) {
this.tasks = tasks;
}
}
My XmlAdapter is:
public class TaskAdapter extends XmlAdapter<String, Task> {
#Override
public Task unmarshal(String v) throws Exception {
return null;
}
#Override
public String marshal(Task t) throws Exception {
JAXBContext context = JAXBContext.newInstance(Task.class);
Marshaller m = context.createMarshaller();
m.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, Boolean.TRUE);
m.setProperty(Marshaller.JAXB_FRAGMENT, true);
m.setProperty("jaxb.encoding", "Unicode");
StringWriter sw = new StringWriter();
if ("".equals(t.getNotes()) && "".equals(t.getHourCount()) && "".equals(t.getTaskCode())) {
return null;
}
m.marshal(t, sw);
String s = sw.toString();
s = s.substring(s.indexOf("<TACHE>") + 7, s.indexOf("</TACHE>"));
System.out.println(s);
return s;
}
}
System.out.println of XmlAdapter print:
<NB_HEURE>7.75</NB_HEURE>
<NOTES>foo</NOTES>
<CODE_TACHE>1234</CODE_TACHE>
but, when I marshal global POJO, I have:
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
...
<TACHES>
<TACHE>
<NB_HEURE>7.75</NB_HEURE>
<NOTES>foo</NOTES>
<CODE_TACHE>1234</CODE_TACHE>
</TACHE>
</TACHES>
...
My is:
JAXBContext context = JAXBContext.newInstance(MoReports.class);
Marshaller m = context.createMarshaller();
m.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, Boolean.TRUE);
StringWriter sw = new StringWriter();
m.marshal(moReports, sw);
xmlString = sw.toString();
Step 1
I do not use 'TaskAdapter extends XmlAdapter'
Step 2
I set JAXB_FORMATTED_OUTPUT at FALSE
m.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, Boolean.FALSE);
Step 3
and I add this code post marshal
xmlString = removeAllEmptyXmlTags(xmlString);
public String removeAllEmptyXmlTags(String xml) {
final String regex1 = "<([a-zA-Z0-9-\\_]*)[^>]*/>";
final String regex2 = "<([a-zA-Z0-9-\\_]*)[^>]*>\\s*</\\1>";
final Pattern pattern1 = Pattern.compile(regex1);
final Pattern pattern2 = Pattern.compile(regex2);
String xmlString = xml;
Matcher matcher1;
Matcher matcher2;
do {
xmlString = xmlString.replaceAll(regex1, "").replaceAll(regex2, "");
matcher1 = pattern1.matcher(xmlString);
matcher2 = pattern2.matcher(xmlString);
} while (matcher1.find() || matcher2.find());
return xmlString;
}
Step 4
and I add this code post marshal
xmlString = xmlIndent(xmlString, false);
public String xmlIndent(String xml, Boolean ommitXmlDeclaration) {
Writer outxml = new StringWriter();
try {
DocumentBuilder db = DocumentBuilderFactory.newInstance().newDocumentBuilder();
Document doc = db.parse(new InputSource(new StringReader(xml)));
OutputFormat format = new OutputFormat(doc);
format.setIndenting(true);
format.setIndent(2);
format.setOmitXMLDeclaration(ommitXmlDeclaration);
format.setLineWidth(Integer.MAX_VALUE);
XMLSerializer serializer = new XMLSerializer(outxml, format);
serializer.serialize(doc);
} catch (ParserConfigurationException | SAXException | IOException e) {
return xml;
}
return outxml.toString();
}
Result is OK:
<TACHES>
<TACHE>
<NB_HEURE>7.75</NB_HEURE>
<NOTES>foo</NOTES>
<CODE_TACHE>1234</CODE_TACHE>
</TACHE>
</TACHES>

Convert Java JAXB object to JSON using Jackson or MOXy dependency

I am working on an API that will return an JSON response from an XML source. I have used RestTemplate and JAXB to get the XML string from the source and then used StringReader and Unmarshaller to create the Java object. The object looks like this;
#XmlRootElement(name="ItemSearchResponse", namespace="http://webservices.amazon.com/AWSECommerceService/2011-08-01") //
#XmlAccessorType(XmlAccessType.FIELD)
public class SampleXML {
#XmlAccessorType(XmlAccessType.FIELD)
#XmlType
public static class OperationRequest {
#XmlTransient
private String RequestId;
#XmlElement(name="RequestId")
public void setRequestId(String id) {
this.RequestId = id;
}
public String getRequestId() {
return RequestId;
}
...
This is the code that should return the JSON string to the browser;
#RequestMapping("/samplexml2")
public SampleXML CreateXMLFile2 () throws EncoderException, FileNotFoundException, SAXException {
try {
String requestUrl = null;
RestTemplate restTemplate = new RestTemplate();
restTemplate.setErrorHandler(new ResponseErrorHandler());
String decodedUrl = "http://localhost:8080/sample.xml";
String response = restTemplate.getForObject(decodedUrl, String.class);
//Prepare JAXB objects
JAXBContext jaxbContext = JAXBContext.newInstance(SampleXML.class);
Unmarshaller u = jaxbContext.createUnmarshaller();
//Create an XMLReader to use with our filter
XMLReader reader = XMLReaderFactory.createXMLReader();
//Create the filter (to add namespace) and set the xmlReader as its parent.
NamespaceFilter inFilter = new NamespaceFilter("http://webservices.amazon.com/AWSECommerceService/2011-08-01", true);
inFilter.setParent(reader);
//Prepare the input, in this case a java.io.File (output)
InputSource is = new InputSource(new StringReader(response));
//Create a SAXSource specifying the filter
SAXSource source = new SAXSource(inFilter, is);
//Do unmarshalling
SampleXML myJaxbObject = (SampleXML) u.unmarshal(source);
//Convert to myJaxbObject to JSON string here;
return myJaxbObject;
} catch (JAXBException e) {
e.printStackTrace();
return null;
}
}
I want to write the conversation at this line; //Convert to myJaxbObject to JSON string here;
I have read many articles that point to this library; https://github.com/FasterXML/jackson-module-jaxb-annotations But I have not been able to use it successfully.
I would like an example that uses Jackson or MOXy dependencies
Have you tried simply changing your RequestMapping to #RequestMapping(value = "/samplexml2", produces = MediaType.APPLICATION_JSON_VALUE)?

Would like unknown attributes to error when Jaxb unmarshalling

I'm using Jaxb to unmarshal XML into a java object. I need to know when new attributes/elements are in the XML and fail. However by default the unmarshaller is ignoring new elements/attributes.
Is there a configuration I can set to fail when new attributes/elements exist in the XML that are not specified in the POJO?
My POJO:
import javax.xml.bind.annotation.XmlAttribute;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlRootElement;
#XmlRootElement(name = "ROOT")
public class Model {
private A a;
public A getA() {
return a;
}
#XmlElement(name = "A")
public void setA(A a) {
this.a = a;
}
static class A {
String country;
public String getCountry() {
return country;
}
#XmlAttribute(name = "Country")
public void setCountry(String country) {
this.country = country;
}
}
}
Code to unmarshal:
JAXBContext jaxbContext = JAXBContext.newInstance(Model.class);
Unmarshaller jaxbUnmarshaller = jaxbContext.createUnmarshaller();
String sample = "<ROOT>" +
"<A Country=\"0\" NewAttribute=\"0\"></A>" +
"<NEWELEMENT> </NEWELEMENT>" +
"</ROOT>";
InputStream stream = new ByteArrayInputStream(sample.getBytes(StandardCharsets.UTF_8));
Object unmarshal = jaxbUnmarshaller.unmarshal(stream);
You need to call Unmarshaller.setEventHandler() to make invalid XML content fail.
You can block unexpected content by enabling XML Schema validation on the Unmarshaller. If you don't already have an XML Schema for your POJO, you can generate one at runtime from the JAXBContext, build a Schema object and then set it on the Unmarshaller. By default the Unmarshaller will throw an exception if the XML document isn't valid with respect to the schema.
Here's an example of how to do that:
JAXBContext jaxbContext = JAXBContext.newInstance(Model.class);
// Generate XML Schema from the JAXBContext
final List<ByteArrayOutputStream> schemaDocs = new ArrayList<ByteArrayOutputStream>();
jaxbContext.generateSchema(new SchemaOutputResolver() {
#Override
public Result createOutput(String namespaceUri, String suggestedFileName)
throws IOException {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
StreamResult sr = new StreamResult(baos);
schemaDocs.add(baos);
sr.setSystemId(suggestedFileName);
return sr;
}
});
SchemaFactory sf = SchemaFactory.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI);
int size = schemaDocs.size();
Source[] schemaSources = new Source[size];
for (int i = 0; i < size; ++i) {
schemaSources[i] = new StreamSource(
new ByteArrayInputStream(schemaDocs.get(i).toByteArray()));
}
Schema s = sf.newSchema(schemaSources);
// Set the JAXP Schema object on your Unmarshaller.
Unmarshaller jaxbUnmarshaller = jaxbContext.createUnmarshaller();
jaxbUnmarshaller.setSchema(s);
String sample = "<ROOT>" +
"<A Country=\"0\" NewAttribute=\"0\"></A>" +
"<NEWELEMENT> </NEWELEMENT>" +
"</ROOT>";
InputStream stream = new ByteArrayInputStream(sample.getBytes("UTF-8"));
Object unmarshal = jaxbUnmarshaller.unmarshal(stream);
Combine this with the ValidationEventHandler (set through Unmarshaller.setEventHandler()) suggested in the previous answer if you wish to be notified about multiple errors or filter out validation errors that you want to tolerate.

Unmarshal JSON String to some "Object" using MOXy

I'm trying to write a method to pretty print JSON Strings, using MOXy. So what I want is to have a method like this
public String formatJson(String input) { ... }
I think the way to go is to parse the String to a generic Object (Something like a SAX-Document, or the kind), and then marshal this Object back to JSON using some formatting properties (which is not the problem :-) ).
The Problem is, when reading the JSON-String-Input, I don't have a Class to unmarshal to (as I want the method to be as generic as possible).
[edited] GSON and Jackson examples removed, as only MOXy is the question.
I tried this:
public static String toFormattedJson(final String jsonString) {
String formatted;
try {
JAXBContext jaxbContext = JAXBContextFactory.createContext(new Class[] { JAXBElement.class }, null);
Unmarshaller unmarshaller = jaxbContext.createUnmarshaller();
unmarshaller.setProperty(MEDIA_TYPE, MediaType.APPLICATION_JSON);
unmarshaller.setProperty(JSON_INCLUDE_ROOT, true);
StringReader reader = new StringReader(jsonString);
Object element = unmarshaller.unmarshal(reader); // Exception is thrown here
formatted = toFormattedJson(element);
} catch (final JAXBException e) {
formatted = jsonString;
}
return formatted;
}
but I get an this Exception
javax.xml.bind.UnmarshalException
- with linked exception:
[java.lang.ClassCastException: org.eclipse.persistence.internal.oxm.record.SAXUnmarshallerHandler cannot be cast to org.eclipse.persistence.internal.oxm.record.UnmarshalRecord]
So how can I read an arbitrary JSON String in to a Java Object, if I don't have any Class for that specific String?
Update:
This is the method used to format an Object into a JSON String:
private static String toFormattedJson(Object obj) {
String result;
try (StringWriter writer = new StringWriter()) {
final JAXBContext jaxbContext = JAXBContextFactory.createContext(new Class[] { obj.getClass() }, null);
final Marshaller marshaller = jaxbContext.createMarshaller();
marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
marshaller.setProperty(JAXBContextProperties.MEDIA_TYPE, MediaType.APPLICATION_JSON);
marshaller.setProperty(MarshallerProperties.JSON_REDUCE_ANY_ARRAYS, false);
marshaller.setProperty(MarshallerProperties.JSON_MARSHAL_EMPTY_COLLECTIONS, false);
marshaller.setProperty(JAXBContextProperties.JSON_WRAPPER_AS_ARRAY_NAME, false);
marshaller.setProperty(JAXBContextProperties.JSON_INCLUDE_ROOT, true);
marshaller.marshal(obj, writer);
writer.flush();
result = writer.toString();
} catch (JAXBException | IOException e) {
result = obj.toString();
}
return result;
}
And using now the code from below (Martin Vojtek), when I try to format
String jsonString = "{\"p\" : [ 1, 2, 3]}";
I get:
{
"p" : "1"
}
You can specify String as the unmarshal target:
public static void main(String[] args) {
System.out.println(toFormattedJson("[{\"test\":true}, \"ok\", [\"inner\",1]]"));
}
public static String toFormattedJson(final String jsonString) {
String formatted;
try {
JAXBContext jaxbContext = JAXBContextFactory.createContext(new Class[] { JAXBElement.class }, null);
System.out.println("jaxbContext="+jaxbContext);
Unmarshaller unmarshaller = jaxbContext.createUnmarshaller();
unmarshaller.setProperty(JAXBContextProperties.MEDIA_TYPE, MediaType.APPLICATION_JSON);
unmarshaller.setProperty(JAXBContextProperties.JSON_INCLUDE_ROOT, true);
StringReader reader = new StringReader(jsonString);
Object element = unmarshaller.unmarshal(new StreamSource(reader), String.class);
formatted = toFormattedJsonElement(element);
} catch (final JAXBException e) {
e.printStackTrace();
formatted = jsonString;
}
return formatted;
}
private static String toFormattedJsonElement(Object element) {
return "formatted: " + element;
}

How to add suffix to XmlRootElement

I've tried this:
package-info.java
#XmlSchema(xmlns=
{
#XmlNs(prefix="Person", namespaceURI="sample.url.something"),
#XmlNs(prefix="xsi", namespaceURI="http://www.w3.org/2001/XMLSchema-instance")
})
Java
#XmlRootElement(name = "Person:sampleData")
public class Person {
private static String path = "files/test.xml";
#XmlElement()
public String Name;
#XmlElement()
public int Age;
public Person(){}
public Person(String name, int age){
this.Name = name;
this.Age = age;
}
public static String PersonToXMLString(Person person) throws JAXBException
{
JAXBContext jc = JAXBContext.newInstance(Person.class);
StringWriter sw = new StringWriter();
Marshaller marshaller = jc.createMarshaller();
marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
marshaller.setProperty(Marshaller.JAXB_SCHEMA_LOCATION, "somelocation.xsd");
marshaller.marshal(person, sw);
return sw.toString();
}
public static Person XMLStringToPerson() throws JAXBException
{
JAXBContext jaxbContext = JAXBContext.newInstance(Person.class);
Unmarshaller unmarshaller = jaxbContext.createUnmarshaller();
Person Person = (Person) unmarshaller.unmarshal(new File(path));
return Person;
}
public static void WriteXMLStringFile(String xml) throws IOException
{
File file = new File(path);
try (FileOutputStream fop = new FileOutputStream(file)) {
if (!file.exists()) {
file.createNewFile();
}
byte[] contentInBytes = xml.getBytes();
fop.write(contentInBytes);
fop.flush();
fop.close();
} catch (IOException e) {
e.printStackTrace();
}
}
public static String ReadXmlStringFromFile() throws IOException
{
BufferedReader br = new BufferedReader(new FileReader(new File(path)));
String line;
StringBuilder sb = new StringBuilder();
while ((line = br.readLine()) != null) {
sb.append(line.trim());
}
return sb.toString();
}
public static void main(String[] args) throws JAXBException, IOException
{
Person user = new Person("User",23);
String xml = user.PersonToXMLString(user);
System.out.println(xml);
user.WriteXMLStringFile(xml);
xml = user.ReadXmlStringFromFile();
//used to unmarshall xml to Person object
Person person = user.XMLStringToPerson();
System.out.println(person.Name);
}
}
XML
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<Person:sampleData xmlns:Person="sample.url.something" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<Name>User</Name>
<Age>23</Age>
</Person:sampleData>
If i do something like this i get exception on unmarshalling:
Exception in thread "main" javax.xml.bind.UnmarshalException: unexpected element (uri:"sample.url.something", local:"sampleData").
Expected elements are <{}Person:sampleData>`
FYI: I can not modify the XML.
Any help gratefully received!
You can do the following:
package-info.java
Your #XmlSchema annotation should look something like the following on your package-info class. Since elementFormDefault is specified as UNQUALIFIED the namespace will only be applied to global elements (in JAXB those corresponding to the #XmlRootElement). Note a JAXB impl is not required to use the prefix included in the #XmlSchema when marshalling XML.
#XmlSchema(
elementFormDefault=XmlNsForm.UNQUALIFIED,
namespace="sample.url.something",
xmlns={
#XmlNs(prefix="Person", namespaceURI="sample.url.something")
}
)
package com.example;
import javax.xml.bind.annotation.*;
Person
The #XmlRootElement annotation should not include the prefix.
package com.example;
import javax.xml.bind.annotation.*;
#XmlRootElement(name="sampleData")
public class Person {
For More Information
You can read more about controlling namespace prefixes in JAXB on my blog:
http://blog.bdoughan.com/2011/11/jaxb-and-namespace-prefixes.html

Categories

Resources