Gson ignores my fields while converting - java

I created a model:
public class UserRequest extends DefaultRequest {
public String username;
public String password;
public String id;
public UserRequest(String username, String password) {
this.username = username;
this.password = password;
}
}
And I'm calling it like:
//code truncated
UserRequest userRequest = new UserRequest(username,password);
response = getRestClient().sysInitApp(userRequest).execute();
//code truncated
And then I print out request body, instead of:
{
"username":"farid",
"password":"passfarid",
"id":null
}
I get:
{
"username":"farid",
"password":"passfarid"
}
I would appreciate any help with this issue.

from the GsonBuilder javadocs... you can use GsonBuilder to construct your Gson instance, and opt in to have null values serialized as so:
Gson gson = new GsonBuilder()
.serializeNulls()
.create();

Not too familiar with Gson, but I don't think Gson would write null values to an json file. If you initialize the id like:
String id = "";
you may get an empty string in there. But you will not get a null value into a .xml file.

An example of how to enforce outputting values even if null. It will output the empty string (or "{}" if an object) instead of null and ignore transients:
package unitest;
import java.io.IOException;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.google.gson.TypeAdapter;
import com.google.gson.TypeAdapterFactory;
import com.google.gson.reflect.TypeToken;
import com.google.gson.stream.JsonReader;
import com.google.gson.stream.JsonToken;
import com.google.gson.stream.JsonWriter;
public class TheResponse<T> {
private String status;
private String message;
private T data;
transient private String resource;
public static void main(String[] args) {
TheResponse<String> foo = new TheResponse<String>();
//TheResponse<Baz> foo = new TheResponse<Baz>();
foo.status = "bar";
foo.data = "baz";
Gson gson = new GsonBuilder().registerTypeAdapter(TheResponse.class, new GenericAdapter()).create();
System.out.println(gson.toJson(foo).toString());
}
public static class GenericAdapter extends TypeAdapter<Object> {
#Override
public void write(JsonWriter jsonWriter, Object o) throws IOException {
recursiveWrite(jsonWriter, o);
}
private void recursiveWrite(JsonWriter jsonWriter, Object o) throws IOException {
jsonWriter.beginObject();
for (Field field : o.getClass().getDeclaredFields()) {
boolean isTransient = Modifier.isTransient(field.getModifiers());
if (isTransient) {
continue;
}
Object fieldValue = null;
try {
field.setAccessible(true);
fieldValue = field.get(o);
} catch (IllegalArgumentException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
jsonWriter.name(field.getName());
if (fieldValue != null && fieldValue.getClass() != String.class) {
recursiveWrite(jsonWriter, fieldValue);
continue;
}
if (fieldValue == null) {
if (field.getType() == String.class)
jsonWriter.value("");
else {
jsonWriter.jsonValue("{}");
}
} else {
jsonWriter.value(fieldValue.toString());
}
}
jsonWriter.endObject();
}
#Override
public Object read(JsonReader jsonReader) throws IOException {
// todo
return null;
}
}
}

Related

ProducerTemplate. Camel. How to add attachment

Can someone tell me how to add an attachment using ProducerTemplate?
I have been searching but I can not find an answer to my case.
I am using Camen 2.1 and I have these three clases:
MailSender2.java
import java.util.HashMap;
import java.util.Map;
import java.util.ResourceBundle;
import org.apache.camel.Exchange;
import org.apache.camel.ExchangePattern;
import org.apache.camel.ProducerTemplate;
public class MailSender2 extends TypeMail{
private static final ResourceBundle RES = ResourceBundle.getBundle("mail");
protected static final String MAIL_NOTIFICATION_ENDPOINT=RES.getString("mail.host.location").trim()+":"+RES.getString("mail.port").trim();
private Map<String, Object> header;
public MailSender2() {
this.header=new HashMap<>();
}
public void send(ProducerTemplate template) {
this.header.put("From", this.getT_from());
this.header.put("To", this.getT_to());
this.header.put("Subject", this.getT_subject());
this.header.put(Exchange.CONTENT_TYPE, "text/html; charset=UTF-8");
//this.getF_ficher() <-- I have here the file to attach
//this.getT_ficnon() <-- I have here the name ot the file
//this.getT_ficext() <-- I have here the extension ot the file
template.sendBodyAndHeaders(MAIL_NOTIFICATION_ENDPOINT, this.getT_mensaje(), header);
}
}
TypeMail.java:
public class TypeMail {
private String t_id;
private String t_from;
private String t_to;
private String t_subject;
private String t_mensaje;
private byte[] f_ficher;
private String t_ficnon;
private String t_ficext;
public String getT_id() {
return t_id;
}
public void setT_id(String t_id) {
this.t_id = t_id;
}
public String getT_from() {
return t_from;
}
public void setT_from(String t_from) {
this.t_from = t_from;
}
public String getT_to() {
return t_to;
}
public void setT_to(String t_to) {
this.t_to = t_to;
}
public String getT_subject() {
return t_subject;
}
public void setT_subject(String t_subject) {
this.t_subject = t_subject;
}
public String getT_mensaje() {
return t_mensaje;
}
public void setT_mensaje(String t_mensaje) {
this.t_mensaje = t_mensaje;
}
public byte[] getF_ficher() {
return f_ficher;
}
public void setF_ficher(byte[] f_ficher) {
this.f_ficher = f_ficher;
}
public String getT_ficnon() {
return t_ficnon;
}
public void setT_ficnon(String t_ficnon) {
this.t_ficnon = t_ficnon;
}
public String getT_ficext() {
return t_ficext;
}
public void setT_ficext(String t_ficext) {
this.t_ficext = t_ficext;
}
}
MailCommunicationTransformer.java:
import org.apache.camel.CamelContext;
import org.apache.camel.Exchange;
import org.apache.camel.ProducerTemplate;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.ws.soap.client.SoapFaultClientException;
public class MailCommunicationTransformer {
MailSender2 mailSender = null;
static Logger logger = LoggerFactory.getLogger(MailCommunicationTransformer.class);
public MailCommunicationTransformer()
{
}
public MailLog transform(Object actualMessage, Exchange exchange, CamelContext context)
{
mailSender = exchange.getIn().getBody(MailSender2.class);
try {
MailSenderDAO mailSenderDAO = (MailSenderDAO)context.getRegistry().lookup("MailSenderDAO");
mailSenderDAO.validarInput(mailSender);
if (mailSender!=null) {
ProducerTemplate template=exchange.getContext().createProducerTemplate();
try {
mailSender.send(template);
}
catch (Throwable ex) {
ex.printStackTrace();
exchange.setProperty(Exchange.EXCEPTION_CAUGHT,ex);
}
}
}catch (MailException me) {
me.printStackTrace();
exchange.setProperty(Exchange.EXCEPTION_CAUGHT,me);
}
Throwable e = exchange.getProperty(Exchange.EXCEPTION_CAUGHT,
Throwable.class);
String response = "OK";
if (e != null) {
StringBuffer mensaje = new StringBuffer();
if (e instanceof SoapFaultClientException) {
mensaje.append("MAIL fault exception: CLIENT. ");
} else {
mensaje.append("MAIL fault exception: MAIL. ");
}
logger.info("MailCommunicationTransformer",e);
while (e != null) {
e.printStackTrace();
mensaje.append(e.getMessage());
e = e.getCause();
}
response = mensaje.toString();
}
MailLog log = new MailLog(mailSender, response); //, protocolo
return log;
}
}
In TypeMail I have the file in f_ficher, and the fileName (t_ficnon) and extension (t_ficext), but I can not find how to attach this file in MailSender2 before template.sendBodyAndHeaders(.....)
Any help would be very appreciated.
Regards.
Perhaps I don't fully understand your question, but the ProducerTemplate don't know about the message type.
You just send a body and perhaps also headers to an endpoint.
Therefore the body just needs to be a fully constructed MimeMessage object as documented in the Camel Mail docs.
You can simply construct the mail message with Java and then use the object with the ProducerTemplate (what you already do).
template.sendBodyAndHeaders("your-smtp-endpoint", yourMimeMessageInstance, yourHeaderMap);
Thanks for the answer!
But, finally, I could do it this way:
new class EmailProcessor.java
import java.io.File;
import java.io.IOException;
import java.net.URL;
import java.nio.file.Files;
import java.util.Objects;
import java.util.ResourceBundle;
import javax.activation.DataHandler;
import org.apache.camel.Exchange;
import org.apache.camel.Message;
import org.apache.camel.Processor;
import org.apache.commons.codec.binary.Base64;
public class EmailProcessor implements Processor {
// Atributos de la clase
private TypeMail typeMail;
public EmailProcessor(TypeMail typeMail) {
this.typeMail = typeMail;
}
#Override
public void process(Exchange exchange) throws Exception {
Message ms = exchange.getIn();
ms.setHeader("From", this.typeMail.getT_from());
ms.setHeader("To", this.typeMail.getT_to());
ms.setHeader("Subject", this.typeMail.getT_subject());
ms.setHeader(Exchange.CONTENT_TYPE, "text/html; charset=UTF-8");
ms.setBody("<p style='font-family: Calibri;'>" + this.typeMail.getT_mensaje() + "</p>");
if (this.typeMail.getF_ficher() != null) {
String mimeType = "application/pdf";
if ("zip".equals(typeMail.getT_ficext())) {
mimeType = "application/zip";
}
ms.addAttachment(typeMail.getT_ficnom() + "." + typeMail.getT_ficext(), new DataHandler(typeMail.getF_ficher(), mimeType));
}
}
}
MailSender.java:
import java.util.ResourceBundle;
import org.apache.camel.ExchangePattern;
import org.apache.camel.ProducerTemplate;
public class MailSender extends TypeMail{
private static final ResourceBundle RES = ResourceBundle.getBundle("mail");
protected static final String MAIL_NOTIFICATION_ENDPOINT=RES.getString("mail.host.location").trim()+":"+RES.getString("mail.port").trim();
public MailSender() {
}
public void send(ProducerTemplate template) {
template.send(MAIL_NOTIFICATION_ENDPOINT, ExchangePattern.InOnly, new EmailProcessor(this));
}
}

how to wrap exception on jackson serialization

How to serialize an object with Jackson if one of getters is throwing an exception?
Example:
public class Example {
public String getSomeField() {
//some logic which will throw in example NPE
throw new NullPointerException();
}
}
Ideally I would like to get JSON:
{"someField":"null"}
or
{"someField":"NPE"}
Probably the most generic way would be implementing custom BeanPropertyWriter. You can register it by creating BeanSerializerModifier class. Below example shows how to do that.
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.databind.BeanDescription;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationConfig;
import com.fasterxml.jackson.databind.SerializerProvider;
import com.fasterxml.jackson.databind.module.SimpleModule;
import com.fasterxml.jackson.databind.ser.BeanPropertyWriter;
import com.fasterxml.jackson.databind.ser.BeanSerializerModifier;
import java.util.List;
import java.util.stream.Collectors;
public class JsonApp {
public static void main(String[] args) throws Exception {
SimpleModule module = new SimpleModule();
module.setSerializerModifier(new BeanSerializerModifier() {
#Override
public List<BeanPropertyWriter> changeProperties(SerializationConfig config, BeanDescription beanDesc, List<BeanPropertyWriter> beanProperties) {
if (beanDesc.getBeanClass() == Response.class) {
return beanProperties.stream()
.map(SilentExceptionBeanPropertyWriter::new)
.collect(Collectors.toList());
}
return super.changeProperties(config, beanDesc, beanProperties);
}
});
ObjectMapper mapper = new ObjectMapper();
mapper.registerModule(module);
System.out.println(mapper.writeValueAsString(new Response(1, "ONE")));
System.out.println(mapper.writeValueAsString(new Response(-1, "MINUS")));
System.out.println(mapper.writeValueAsString(new Response(-1, null)));
}
}
class SilentExceptionBeanPropertyWriter extends BeanPropertyWriter {
public SilentExceptionBeanPropertyWriter(BeanPropertyWriter base) {
super(base);
}
#Override
public void serializeAsField(Object bean, JsonGenerator gen, SerializerProvider prov) throws Exception {
try {
super.serializeAsField(bean, gen, prov);
} catch (Exception e) {
Throwable cause = e.getCause();
gen.writeFieldName(_name);
gen.writeString(cause.getClass().getName() + ":" + cause.getMessage());
}
}
}
class Response {
private int count;
private String message;
public Response(int count, String message) {
this.count = count;
this.message = message;
}
public int getCount() {
if (count < 0) {
throw new IllegalStateException("Count is less than ZERO!");
}
return count;
}
public void setCount(int count) {
this.count = count;
}
public String getMessage() {
if (message == null) {
throw new NullPointerException("message can not be null!");
}
return message;
}
public void setMessage(String message) {
this.message = message;
}
}
Above example prints:
{"count":1,"message":"ONE"}
{"count":"java.lang.IllegalStateException:Count is less than ZERO!","message":"MINUS"}
{"count":"java.lang.IllegalStateException:Count is less than ZERO!","message":"java.lang.NullPointerException:message can not be null!"}

Java Reflection accessing public fields

I am writing a custom Serializer (Jackson JSON) for List class, this list could be inferred with different class types, so I'll need to grab object fields values using reflection.
Note, all this classes has public values (no setters and getters), so Invoking the getter will not be an option.
This is what I get so far:
package com.xxx.commons.my.serializer;
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.databind.SerializerProvider;
import com.fasterxml.jackson.databind.ser.std.StdSerializer;
import org.apache.commons.lang3.reflect.FieldUtils;
import java.io.IOException;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.lang.reflect.Type;
import java.math.BigDecimal;
import java.util.List;
public class ListSerializer extends StdSerializer<List> {
public ListSerializer() {
super(List.class);
}
#Override
public void serialize(List aList, JsonGenerator jsonGenerator, SerializerProvider serializerProvider) throws IOException {
if (aList != null) {
jsonGenerator.writeStartObject();
for (int index = 0 ; index < aList.size(); index++) {
try {
Object next = aList.get(index);
List<Field> fields = FieldUtils.getAllFieldsList(next.getClass());
Object object = next.getClass().newInstance();
for (int j = 0 ; j < fields.size(); j ++ ) {
jsonGenerator.writeObjectField(String.format("%s[%s]",fields.get(j).getName(),index) , object);
}
} catch (Exception e) {
e.printStackTrace();
}
}
jsonGenerator.writeEndObject();
}
}
}
MyTest
package com.xxx.commons.my.serializer;
import com.fasterxml.jackson.annotation.JsonAutoDetect;
import com.fasterxml.jackson.annotation.PropertyAccessor;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature;
import com.fasterxml.jackson.databind.module.SimpleModule;
import org.junit.Test;
public class ListSerializerTest {
private ObjectMapper mapper = new ObjectMapper();
#Test
public void test() throws Exception {
SimpleModule module = new SimpleModule();
module.addSerializer(new ListSerializer());
mapper.configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false);
mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
mapper.registerModule(module);
MyTempClassParent parent = new MyTempClassParent();
parent.mylist.add(new MyTempClass("1","2","3"));
String json = mapper.writeValueAsString(parent);
System.out.println(json);
}
}
Example classes:
public class MyTempClass {
public MyTempClass() {
}
public MyTempClass(String value1, String value2, String value3) {
this.valueA = value1;
this.valueB = value2;
this.valueC = value3;
}
public String valueA;
public String valueB;
public String valueC;
}
public class MyTempClassParent {
public List<MyTempClass> mylist = new LinkedList<>();
}
Any ideas or alternatives for writing this ?
Maybe you should just use ObjectMapper with setting property accessor to get access to every field:
ObjectMapper mapper = new ObjectMapper();
mapper.setVisibility(PropertyAccessor.FIELD, JsonAutoDetect.Visibility.ANY);
MyDtoAccessLevel dtoObject = new MyDtoAccessLevel();
String dtoAsString = mapper.writeValueAsString(Arrays.asList(dtoObject));
System.out.println(dtoAsString);
result:
[{"stringValue":null,"intValue":0,"floatValue":0.0,"booleanValue":false}]
dto:
class MyDtoAccessLevel {
private String stringValue;
int intValue;
protected float floatValue;
public boolean booleanValue;
// NO setters or getters
--edit
For getting values from objects by reflection:
#Override
public void serialize(List aList, JsonGenerator jsonGenerator, SerializerProvider serializerProvider) throws IOException {
if (aList != null) {
jsonGenerator.writeStartObject();
for (int index = 0 ; index < aList.size(); index++) {
try {
Object next = aList.get(index);
List<Field> fields = FieldUtils.getAllFieldsList(next.getClass());
for (int j = 0 ; j < fields.size(); j ++ ) {
jsonGenerator.writeObjectField(String.format("%s[%s]",fields.get(j).getName(),index) , fields.get(j).get(next));
}
} catch (Exception e) {
e.printStackTrace();
}
}
jsonGenerator.writeEndObject();
}
}
Please write in question, what do you want to have in output.

Weird error when using foreach loop in java

I am working on a little todolist-program and i'm getting a weird bug that i never had before. I have 4 classes: 1 POJO class that contains the todo-data:
public class Todo implements Comparable {
private String title;
private String task;
private boolean done;
public Todo(String title, String task) {
this.title = title;
this.task = task;
}
public String getTitle() {
return title;
}
public void setTitle(String newTitle) {
title = newTitle;
}
public String getTask() {
return task;
}
public void setTask(String newTask) {
task = newTask;
}
public boolean isDone() {
return done;
}
public void setDone(boolean isDone) {
done = isDone;
}
public int compareTo(Object obj) {
Todo todo = (Todo) obj;
return getTitle().compareTo(todo.getTitle());
}
public String toString() {
StringBuilder sb = new StringBuilder();
sb.append("Todo {\n");
sb.append("Title: \"");
sb.append(getTitle() + "\";\n");
sb.append("Task: \"");
sb.append(getTask() + "\";\n");
sb.append("}");
return sb.toString();
}
}
Then I have a class that stores and loads my todos:
import java.io.BufferedReader;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.util.List;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
public class ListStorage {
Gson gson = new GsonBuilder().setPrettyPrinting().create();
FileWriter writer;
BufferedReader reader;
public void storeList(List list, String filename) throws IOException {
String json = gson.toJson(list);
writer = new FileWriter(filename);
writer.write(json);
writer.close();
}
public List loadList(String filename) throws FileNotFoundException {
reader = new BufferedReader(new FileReader(filename));
List list = gson.fromJson(reader, List.class);
return list;
}
}
Then I have a 'Manager' class that is basically my controller:
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
public class Manager {
private List<Todo> todos = new ArrayList<>();
private ListStorage storage = new ListStorage();
public List getTodos() {
return todos;
}
public void setTodos(List newTodos) {
todos = newTodos;
}
public ListStorage getStorage() {
return storage;
}
public void add(String title, String task) {
todos.add(new Todo(title, task));
sort();
try {
storage.storeList(todos, "todos");
} catch(Exception e) {
e.printStackTrace();
}
}
public void remove(int index) {
todos.remove(index);
sort();
try {
storage.storeList(todos, "todos");
} catch(Exception e) {
e.printStackTrace();
}
}
private void sort() {
Collections.sort(todos);
}
}
And finally there is my main-class for testing my code (The bug seems to be here):
class CLITodo {
public static void main(String[] args) {
Manager man = new Manager();
man.add("Hello", "Bye");
man.add("Foo", "Bar");
try {
man.setTodos(man.getStorage().loadList("todos"));
} catch(Exception e) {
}
java.util.List<Todo> todos = man.getTodos();
for (Todo t : todos) {
System.out.println(t);
}
}
}
The error message I get when I leave the <Todo> in CLITodo class is:
Exception in thread "main" java.lang.ClassCastException: com.google.gson.internal.LinkedTreeMap cannot be cast to Todo at CLITodo.main(CLITodo.java:13)
When I remove <Todo> in CLITodo I get this error:
CLITodo.java:13:19: error: incompatible types
for (Todo t : todos) {
^
required: Todo
found: Object
Why does this error occur? My Manager classes getTodos()-Method returns a List of type Todo yet the compiler tells me that it is just an Object (which it is of course but it is a collection as well, which should actually work).
This is the first time this error occured and I really can't seem to find what is causing it.
When you don't specify what nested type to use to deserialize your JSON, like you do here
List list = gson.fromJson(reader, List.class); // All it knows is that the root json is a List
Gson uses LinkedTreeMap.
What you really want is
List list = gson.fromJson(reader, new TypeToken<List<Todo>>(){}.getType());

Can I re-order an existing XML to adhere to an XSD

We're generating an XML with Java (org.w3c.dom.Node), using essentially
parent.appendChild(doc.createElement(nodeName));
this generates an XML where nodes are sorted by the order of calling the 'appendChild'. The final XML, however, needs to adhere to a given XSD. Our code can ensure that valid value types, mandatory fields etc. are ok. I am however struggling with the node order.
Is there any approach to either:
On insert ensure that the node order matches the XSD
Re-order the entire XML after creation according to the XSD
To clarify:
What we have is:
<myNodeA>...</myNodeA>
<myNodeC>...</myNodeC>
<myNodeB>...</myNodeB>
And what the XSD wants is:
<myNodeA>...</myNodeA>
<myNodeB>...</myNodeB>
<myNodeC>...</myNodeC>
Thanks, Simon
I've done this before by traversing the schema and then pulling relevant pieces from the XML model and streaming it along the way.
There are multiple xsd model libraries to use:
xsom
xerces
xmlschema
Here's an example using xsom (which can be replaced by one of the above) and xom (which can be replaced with dom)
Main:
package main;
import java.io.File;
import java.io.FileOutputStream;
import java.io.OutputStream;
import javax.xml.stream.XMLOutputFactory;
import javax.xml.stream.XMLStreamWriter;
import node.xom.WrappedDocument;
import nu.xom.Builder;
import nu.xom.Document;
import nu.xom.Element;
import reorder.xsom.UncheckedXMLStreamWriter;
import reorder.xsom.XSVisitorWriteOrdered;
import com.sun.xml.xsom.XSElementDecl;
import com.sun.xml.xsom.XSSchemaSet;
import com.sun.xml.xsom.parser.XSOMParser;
public class ReorderXmlToXsd {
public static void main(String[] args) throws Exception {
File unorderedXml = new File("unordered.xml");
File xsd = new File("your.xsd");
File orderedXml = new File("ordered.xml");
XSOMParser p = new XSOMParser();
p.parse(xsd);
XSSchemaSet parsed = p.getResult();
Builder xom = new Builder();
Document unorderedDoc = xom.build(unorderedXml);
Element unorderedRoot = unorderedDoc.getRootElement();
XSElementDecl root = parsed.getElementDecl(
unorderedRoot.getNamespaceURI(),
unorderedRoot.getLocalName());
XMLOutputFactory stax = XMLOutputFactory.newInstance();
try (OutputStream to = new FileOutputStream(orderedXml)) {
XMLStreamWriter using = stax.createXMLStreamWriter(to, "UTF-8");
root.visit(
new XSVisitorWriteOrdered(
new WrappedDocument(unorderedDoc),
new UncheckedXMLStreamWriter(using)));
}
}
}
The actual reordering logic. You will probably have to modify this further. For example, I didn't have to deal with the xsd:any for my project.
package reorder.xsom;
import node.WrappedNode;
import com.sun.xml.xsom.*;
import com.sun.xml.xsom.visitor.XSVisitor;
public class XSVisitorWriteOrdered implements XSVisitor {
private final WrappedNode currNode;
private final UncheckedXMLStreamWriter writeTo;
public XSVisitorWriteOrdered(WrappedNode currNode, UncheckedXMLStreamWriter writeTo) {
this.currNode = currNode;
this.writeTo = writeTo;
}
#Override
public void attributeUse(XSAttributeUse use) {
attributeDecl(use.getDecl());
}
#Override
public void modelGroupDecl(XSModelGroupDecl decl) {
modelGroup(decl.getModelGroup());
}
#Override
public void modelGroup(XSModelGroup model) {
for (XSParticle term : model.getChildren()) {
term.visit(this);
}
}
#Override
public void particle(XSParticle particle) {
XSTerm term = particle.getTerm();
term.visit(this);
}
#Override
public void complexType(XSComplexType complex) {
for (XSAttributeUse use : complex.getAttributeUses()) {
attributeUse(use);
}
XSContentType contentType = complex.getContentType();
contentType.visit(this);
}
#Override
public void elementDecl(XSElementDecl decl) {
String namespaceUri = decl.getTargetNamespace();
String localName = decl.getName();
for (WrappedNode child : currNode.getChildElements(namespaceUri, localName)) {
writeTo.writeStartElement(namespaceUri, localName);
XSType type = decl.getType();
type.visit(new XSVisitorWriteOrdered(child, writeTo));
writeTo.writeEndElement();
}
}
#Override
public void attributeDecl(XSAttributeDecl decl) {
String namespaceUri = decl.getTargetNamespace();
String localName = decl.getName();
WrappedNode attribute = currNode.getAttribute(namespaceUri, localName);
if (attribute != null) {
String value = attribute.getValue();
if (value != null) {
writeTo.writeAttribute(namespaceUri, localName, value);
}
}
}
#Override
public void simpleType(XSSimpleType simpleType) {
String value = currNode.getValue();
if (value != null) {
writeTo.writeCharacters(value);
}
}
#Override
public void empty(XSContentType empty) {}
#Override
public void facet(XSFacet facet) {}
#Override
public void annotation(XSAnnotation ann) {}
#Override
public void schema(XSSchema schema) {}
#Override
public void notation(XSNotation notation) {}
#Override
public void identityConstraint(XSIdentityConstraint decl) {}
#Override
public void xpath(XSXPath xp) {}
#Override
public void wildcard(XSWildcard wc) {}
#Override
public void attGroupDecl(XSAttGroupDecl decl) {}
}
Stax writer:
package reorder.xsom;
import javax.xml.stream.XMLStreamException;
import javax.xml.stream.XMLStreamWriter;
public class UncheckedXMLStreamWriter {
private final XMLStreamWriter real;
public UncheckedXMLStreamWriter(XMLStreamWriter delegate) {
this.real = delegate;
}
public void writeStartElement(String namespaceUri, String localName) {
try {
real.writeStartElement(namespaceUri, localName);
} catch (XMLStreamException e) {
throw new RuntimeException(e);
}
}
public void writeEndElement() {
try {
real.writeEndElement();
} catch (XMLStreamException e) {
throw new RuntimeException(e);
}
}
public void writeAttribute(String namespaceUri, String localName, String value) {
try {
real.writeAttribute(namespaceUri, localName, value);
} catch (XMLStreamException e) {
throw new RuntimeException(e);
}
}
public void writeCharacters(String value) {
try {
real.writeCharacters(value);
} catch (XMLStreamException e) {
throw new RuntimeException(e);
}
}
}
The xml view:
package node;
import java.util.List;
import javax.annotation.Nullable;
public interface WrappedNode {
List<? extends WrappedNode> getChildElements(String namespaceUri, String localName);
#Nullable
WrappedNode getAttribute(String namespaceUri, String localName);
#Nullable
String getValue();
}
XOM implementation:
Document:
package node.xom;
import java.util.Collections;
import java.util.List;
import node.WrappedNode;
import nu.xom.Document;
import nu.xom.Element;
public class WrappedDocument implements WrappedNode {
private final Document d;
public WrappedDocument(Document d) {
this.d = d;
}
#Override
public List<WrappedElement> getChildElements(String namespaceUri, String localName) {
Element root = d.getRootElement();
if (isAt(root, namespaceUri, localName)) {
return Collections.singletonList(new WrappedElement(root));
}
return Collections.emptyList();
}
#Override
public WrappedAttribute getAttribute(String namespaceUri, String localName) {
throw new UnsupportedOperationException();
}
#Override
public String getValue() {
throw new UnsupportedOperationException();
}
#Override
public String toString() {
return d.toString();
}
private static boolean isAt(Element e, String namespaceUri, String localName) {
return namespaceUri.equals(e.getNamespaceURI())
&& localName.equals(e.getLocalName());
}
}
Element:
package node.xom;
import java.util.AbstractList;
import java.util.List;
import node.WrappedNode;
import nu.xom.Attribute;
import nu.xom.Element;
import nu.xom.Elements;
class WrappedElement implements WrappedNode {
private final Element e;
WrappedElement(Element e) {
this.e = e;
}
#Override
public List<WrappedElement> getChildElements(String namespaceUri, String localName) {
return asList(e.getChildElements(localName, namespaceUri));
}
#Override
public WrappedAttribute getAttribute(String namespaceUri, String localName) {
Attribute attribute = e.getAttribute(localName, namespaceUri);
return (attribute != null) ? new WrappedAttribute(attribute) : null;
}
#Override
public String getValue() {
return e.getValue();
}
#Override
public String toString() {
return e.toString();
}
private static List<WrappedElement> asList(final Elements eles) {
return new AbstractList<WrappedElement>() {
#Override
public WrappedElement get(int index) {
return new WrappedElement(eles.get(index));
}
#Override
public int size() {
return eles.size();
}
};
}
}
Attribute:
package node.xom;
import java.util.List;
import node.WrappedNode;
import nu.xom.Attribute;
class WrappedAttribute implements WrappedNode {
private final Attribute a;
WrappedAttribute(Attribute a) {
this.a = a;
}
#Override
public List<WrappedNode> getChildElements(String namespaceUri, String localName) {
throw new UnsupportedOperationException();
}
#Override
public WrappedNode getAttribute(String namespaceUri, String localName) {
throw new UnsupportedOperationException();
}
#Override
public String getValue() {
return a.getValue();
}
#Override
public String toString() {
return a.toString();
}
}
I don't think something like that exists. Simple reordering might be possible, but XML-Schema allows to define so many constraints, that it might be impossible to write a general tool that converts some XML document into a valid document according to some schema.
So, just build the document correctly in the first place.

Categories