java - marshal - ¿Puedo forzar a JAXB a que no convierta "en & quot;, por ejemplo, al calcular a XML?
object to xml java (14)
@ Elliot puede usar esto para habilitar a marshaller para que ingrese en la función characterEscape. Es extraño pero funciona si configura " Unicode " en lugar de "UTF-8". Agregue esto justo antes o después de establecer la propiedad CharacterEscapeHandler.
marshaller.setProperty(Marshaller.JAXB_ENCODING, "Unicode");
Sin embargo , no esté seguro solo revisando su consola dentro de su IDE, ya que debería mostrarse dependiendo de la codificación del área de trabajo. Es mejor verificarlo también desde un archivo como ese:
marshaller.marshal(shipOrder, new File("C://shipOrder.txt"));
Tengo un objeto que se está compaginando a XML utilizando JAXB. Un elemento contiene una cadena que incluye comillas ("). El XML resultante tiene "
donde existió ".
Aunque normalmente se prefiere esto, necesito que mi salida coincida con un sistema heredado . ¿Cómo fuerzo a JAXB a NO convertir las entidades HTML?
-
Gracias por las respuestas. Sin embargo, nunca veo al controlador escape () llamado. ¿Puedes echar un vistazo y ver lo que estoy haciendo mal? ¡Gracias!
package org.dc.model;
import java.io.IOException;
import java.io.Writer;
import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBException;
import javax.xml.bind.Marshaller;
import org.dc.generated.Shiporder;
import com.sun.xml.internal.bind.marshaller.CharacterEscapeHandler;
public class PleaseWork {
public void prettyPlease() throws JAXBException {
Shiporder shipOrder = new Shiporder();
shipOrder.setOrderid("Order''s ID");
shipOrder.setOrderperson("The woman said, /"How ya doin & stuff?/"");
JAXBContext context = JAXBContext.newInstance("org.dc.generated");
Marshaller marshaller = context.createMarshaller();
marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, Boolean.TRUE);
marshaller.setProperty(CharacterEscapeHandler.class.getName(),
new CharacterEscapeHandler() {
@Override
public void escape(char[] ch, int start, int length,
boolean isAttVal, Writer out) throws IOException {
out.write("Called escape for characters = " + ch.toString());
}
});
marshaller.marshal(shipOrder, System.out);
}
public static void main(String[] args) throws Exception {
new PleaseWork().prettyPlease();
}
}
-
La salida es esta:
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<shiporder orderid="Order''s ID">
<orderperson>The woman said, "How ya doin & stuff?"</orderperson>
</shiporder>
y como puede ver, la devolución de llamada nunca se muestra. (Una vez que recibo la devolución de llamada, me preocuparé de que realmente haga lo que quiero).
-
Acabo de hacer mi controlador personalizado como una clase como esta:
import java.io.IOException;
import java.io.StringWriter;
import java.io.Writer;
import com.sun.xml.bind.marshaller.CharacterEscapeHandler;
public class XmlCharacterHandler implements CharacterEscapeHandler {
public void escape(char[] buf, int start, int len, boolean isAttValue,
Writer out) throws IOException {
StringWriter buffer = new StringWriter();
for (int i = start; i < start + len; i++) {
buffer.write(buf[i]);
}
String st = buffer.toString();
if (!st.contains("CDATA")) {
st = buffer.toString().replace("&", "&").replace("<", "<")
.replace(">", ">").replace("''", "'")
.replace("/"", """);
}
out.write(st);
System.out.println(st);
}
}
en el método marshaller simplemente llame:
marshaller.setProperty(CharacterEscapeHandler.class.getName(),
new XmlCharacterHandler());
funciona bien.
Después de probar todas las soluciones anteriores, finalmente llegó a la conclusión.
su lógica de cálculo a través del controlador de escape personalizado.
final StringWriter sw = new StringWriter(); final Class classType = fixml.getClass(); final JAXBContext jaxbContext = JAXBContext.newInstance(classType); final Marshaller marshaller = jaxbContext.createMarshaller(); final JAXBElement<T> fixmsg = new JAXBElement<T>(new QName(namespaceURI, localPart), classType, fixml); marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true); marshaller.setProperty(CharacterEscapeHandler.class.getName(), new JaxbCharacterEscapeHandler()); marshaller.marshal(fixmsg, sw); return sw.toString();
Y el controlador de escape personalizado es el siguiente:
import java.io.IOException; import java.io.Writer; public class JaxbCharacterEscapeHandler implements CharacterEscapeHandler { public void escape(char[] buf, int start, int len, boolean isAttValue, Writer out) throws IOException { for (int i = start; i < start + len; i++) { char ch = buf[i]; out.write(ch); } } }
Esto me funciona después de leer otros mensajes:
javax.xml.bind.JAXBContext jc = javax.xml.bind.JAXBContext.newInstance(object);
marshaller = jc.createMarshaller(); marshaller.setProperty(javax.xml.bind.Marshaller.JAXB_FORMATTED_OUTPUT, true);
marshaller.setProperty(javax.xml.bind.Marshaller.JAXB_ENCODING, "UTF-8"); marshaller.setProperty(CharacterEscapeHandler.class.getName(), new CustomCharacterEscapeHandler());
public static class CustomCharacterEscapeHandler implements CharacterEscapeHandler {
/**
* Escape characters inside the buffer and send the output to the Writer.
* (prevent <b> to be converted <b> but still ok for a<5.)
*/
public void escape(char[] buf, int start, int len, boolean isAttValue, Writer out) throws IOException {
if (buf != null){
StringBuilder sb = new StringBuilder();
for (int i = start; i < start + len; i++) {
char ch = buf[i];
//by adding these, it prevent the problem happened when unmarshalling
if (ch == ''&'') {
sb.append("&");
continue;
}
if (ch == ''"'' && isAttValue) {
sb.append(""");
continue;
}
if (ch == ''/''' && isAttValue) {
sb.append("'");
continue;
}
// otherwise print normally
sb.append(ch);
}
//Make corrections of unintended changes
String st = sb.toString();
st = st.replace("&quot;", """)
.replace("&lt;", "<")
.replace("&gt;", ">")
.replace("&apos;", "'")
.replace("&amp;", "&");
out.write(st);
}
}
}
He estado jugando un poco con tu ejemplo y depurando el código JAXB. Y parece que es algo específico acerca de la codificación UTF-8 utilizada. La propiedad escapeHandler de MarshallerImpl
parece estar configurada correctamente. Sin embargo, no se utiliza en todos los contextos. Si busqué llamadas de MarshallerImpl.createEscapeHandler()
encontré:
public XmlOutput createWriter( OutputStream os, String encoding ) throws JAXBException {
// UTF8XmlOutput does buffering on its own, and
// otherwise createWriter(Writer) inserts a buffering,
// so no point in doing a buffering here.
if(encoding.equals("UTF-8")) {
Encoded[] table = context.getUTF8NameTable();
final UTF8XmlOutput out;
if(isFormattedOutput())
out = new IndentingUTF8XmlOutput(os,indent,table);
else {
if(c14nSupport)
out = new C14nXmlOutput(os,table,context.c14nSupport);
else
out = new UTF8XmlOutput(os,table);
}
if(header!=null)
out.setHeader(header);
return out;
}
try {
return createWriter(
new OutputStreamWriter(os,getJavaEncoding(encoding)),
encoding );
} catch( UnsupportedEncodingException e ) {
throw new MarshalException(
Messages.UNSUPPORTED_ENCODING.format(encoding),
e );
}
}
Tenga en cuenta que en su configuración, la sección superior (...equals("UTF-8")...)
se tiene en cuenta. Sin embargo este no toma el escapeHandler
. Sin embargo, si configura la codificación en cualquier otro, la parte inferior de este método se llama ( createWriter(OutputStream, String)
) y este utiliza escapeHandler
, por lo que EH cumple su función. Entonces, agregando ...
marshaller.setProperty(Marshaller.JAXB_ENCODING, "ASCII");
hace que su CharacterEscapeHandler
personalizado sea llamado. No estoy seguro, pero supongo que este es un error en JAXB.
Interesante pero con cuerdas puedes probar.
Marshaller marshaller = jaxbContext.createMarshaller();
StringWriter sw = new StringWriter();
marshaller.marshal(data, sw);
sw.toString();
Al menos para mí esto no escapa a las citas.
La forma más sencilla, cuando se utiliza la implementación Marshaller de sun, es proporcionar su propia implementación de CharacterEscapeEncoder que no escapa a nada.
Marshaller m = jcb.createMarshaller();
m.setProperty(
"com.sun.xml.bind.marshaller.CharacterEscapeHandler",
new NullCharacterEscapeHandler());
Con
public class NullCharacterEscapeHandler implements CharacterEscapeHandler {
public NullCharacterEscapeHandler() {
super();
}
public void escape(char[] ch, int start, int length, boolean isAttVal, Writer writer) throws IOException {
writer.write( ch, start, length );
}
}
No recomendaría el uso de CharacterEscapeHandler
por las razones mencionadas anteriormente (es una clase interna). En su lugar, puede utilizar Woodstox y suministrar su propia EscapingWriterFactory
a un XMLStreamWriter
. Algo como:
XMLOutputFactory2 xmlOutputFactory = (XMLOutputFactory2)XMLOutputFactory.newFactory();
xmlOutputFactory.setProperty(XMLOutputFactory2.P_TEXT_ESCAPER, new EscapingWriterFactory() {
@Override
public Writer createEscapingWriterFor(Writer w, String enc) {
return new EscapingWriter(w);
}
@Override
public Writer createEscapingWriterFor(OutputStream out, String enc) throws UnsupportedEncodingException {
return new EscapingWriter(new OutputStreamWriter(out, enc));
}
});
marshaller.marshal(model, xmlOutputFactory.createXMLStreamWriter(out);
Un ejemplo de cómo escribir un EscapingWriter
se puede ver en CharacterEscapingTest .
Parece que es posible con la implementación JAXB de Sun , aunque no lo he hecho yo mismo.
Por alguna razón no tengo tiempo de averiguarlo, funcionó para mí al configurar
marshaller.setProperty(Marshaller.JAXB_ENCODING, "utf-8");
A diferencia de usar "UTF-8"
o "Unicode"
Te sugiero que los pruebes, y como dijo @Javatar , comprueba que vayan a archivo usando:
marshaller.marshal(shipOrder, new File("<test_file_path>"));
y abriéndolo con un editor de texto decente como notepad++
Revisé la especificación XML. http://www.w3.org/TR/REC-xml/#sec-references dice "los documentos bien formados no tienen que declarar ninguna de las siguientes entidades: amp, lt, gt, apos, quot." por lo que parece que el analizador XML utilizado por el sistema heredado no es conforme.
(Sé que no resuelve tu problema, pero al menos es bueno poder decir qué componente está roto).
Solución que mi compañero de equipo encontró:
PrintWriter printWriter = new PrintWriter(new FileWriter(xmlFile));
DataWriter dataWriter = new DataWriter(printWriter, "UTF-8", DumbEscapeHandler.theInstance);
marshaller.marshal(request, dataWriter);
En lugar de pasar el xmlFile a marshal (), pase el DataWriter que conoce tanto la codificación como un controlador de escape adecuado, si lo hay.
Nota: dado que DataWriter y DumbEscapeHandler están dentro del paquete com.sun.xml.internal.bind.marshaller, debe arrancar javac.
Yo diría que la forma más fácil de hacerlo es anular CharacterEscapeHandler
:
marshaller.setProperty("com.sun.xml.bind.characterEscapeHandler", new CharacterEscapeHandler() {
@Override
public void escape(char[] ch, int start, int length, boolean isAttVal,
Writer out) throws IOException {
out.write(ch, start, length);
}
});
encontré el mismo problema. Lo solucioné usando xmlWriter en xmlWriter. Hay un método isEscapeText () y setEscapeTest que es verdadero por defecto si no desea la transformación entre <to & lt esa vez que necesita setEscapeTest (false); durante la clasificación
JAXBContext jaxbContext = JAXBContext.newInstance(your class);
Marshaller marshaller = jaxbContext.createMarshaller();
marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
// Create a filter that will remove the xmlns attribute
NamespaceFilter outFilter = new NamespaceFilter(null, false);
// Do some formatting, this is obviously optional and may effect
// performance
OutputFormat format = new OutputFormat();
format.setIndent(true);
format.setNewlines(true);
// Create a new org.dom4j.io.XMLWriter that will serve as the
// ContentHandler for our filter.
XMLWriter writer = new XMLWriter(new FileOutputStream(file), format);
writer.setEscapeText(false); // <----------------- this line
// Attach the writer to the filter
outFilter.setContentHandler(writer);
// marshalling
marshaller.marshal(piaDto, outFilter);
marshaller.marshal(piaDto, System.out);
este cambio writer.setEscapeText (falso); arreglado mi problema espero que esto te sea útil