J'ai un objet en cours de conversion en XML à l'aide de JAXB. Un élément contient une chaîne qui inclut des guillemets ("). Le XML résultant a "
où le" existait.
Même si cela est généralement préférable, ma sortie doit correspondre à un système legacy . Comment forcer JAXB à NE PAS convertir les entités HTML?
-
Merci pour les réponses. Cependant, je ne vois jamais le gestionnaire escape () appelé. Pouvez-vous regarder et voir ce que je fais mal? Merci!
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 sortie est la suivante:
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<shiporder orderid="Order's ID">
<orderperson>The woman said, "How ya doin & stuff?"</orderperson>
</shiporder>
et comme vous pouvez le constater, le rappel n’est jamais affiché. (Une fois que j'aurai reçu le rappel, je n'aurai plus à le faire faire ce que je veux.)
-
Solution trouvée par mon coéquipier:
PrintWriter printWriter = new PrintWriter(new FileWriter(xmlFile));
DataWriter dataWriter = new DataWriter(printWriter, "UTF-8", DumbEscapeHandler.theInstance);
marshaller.marshal(request, dataWriter);
Au lieu de transmettre le fichier xmlFile à marshal (), transmettez le DataWriter qui connaît à la fois l’encodage et un gestionnaire d’échappement approprié, le cas échéant.
Remarque: DataWriter et DumbEscapeHandler étant tous deux contenus dans le package com.Sun.xml.internal.bind.marshaller, vous devez initialiser javac.
Je viens de faire mon gestionnaire personnalisé en tant que classe comme ceci:
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);
}
}
dans la méthode marshaller, appelez simplement:
marshaller.setProperty(CharacterEscapeHandler.class.getName(),
new XmlCharacterHandler());
ça fonctionne bien.
J'ai un peu joué avec votre exemple et débogué le code JAXB. Et il semble que le codage UTF-8 utilisé soit spécifique. La propriété escapeHandler de MarshallerImpl
semble être définie correctement. Cependant, il n'est pas utilisé dans tous les contextes. Si j'ai recherché des appels de MarshallerImpl.createEscapeHandler()
j'ai trouvé:
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 );
}
}
Notez que dans votre configuration, la section supérieure (...equals("UTF-8")...)
est prise en compte. Cependant celui-ci ne prend pas la escapeHandler
. Cependant, si vous définissez le codage sur un autre, la partie inférieure de cette méthode est appelée (createWriter(OutputStream, String)
) et celle-ci utilise escapeHandler
, donc EH joue son rôle . Donc, en ajoutant ...
marshaller.setProperty(Marshaller.JAXB_ENCODING, "ASCII");
fait que votre coutume CharacterEscapeHandler
soit appelée . Pas vraiment sûr, mais je suppose que c'est un peu un bogue dans JAXB.
Je dirais que la meilleure façon de faire est de remplacer 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);
}
});
@ Elliot vous pouvez l'utiliser pour permettre à marshaller d'entrer la fonction characterEscape . C'est étrange mais cela fonctionne si vous définissez " Unicode " au lieu de "UTF-8". Ajoutez-le juste avant ou après la définition de la propriété CharacterEscapeHandler.
marshaller.setProperty(Marshaller.JAXB_ENCODING, "Unicode");
Cependant ne vous assurez pas uniquement en vérifiant votre console dans votre IDE, car elle devrait être affichée dépend du codage de l'espace de travail. Il est préférable de vérifier également à partir d'un fichier comme celui-ci:
marshaller.marshal(shipOrder, new File("C:\\shipOrder.txt"));
j'ai trouvé le même problème J'ai corrigé ceci en utilisant xmlWriter Dans le fichier xmlWriter, il existe une méthode isEscapeText () et setEscapeTest qui est par défaut vrai si vous ne voulez pas de transformation entre <pour & lt cette fois-ci besoin de setEscapeTest (false); pendant le triage
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);
cette modificationwriter.setEscapeText (false);résolu mon problème espérons que cela change pour vous
Cela fonctionne pour moi après avoir lu d'autres articles:
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);
}
}
}
J'ai vérifié la spécification XML. http://www.w3.org/TR/REC-xml/#sec-references dit "les documents bien formés ne doivent déclarer aucune des entités suivantes: amp, lt, gt, apos, quot." il apparaît donc que l'analyseur XML utilisé par le système existant n'est pas conforme.
(Je sais que cela ne résout pas votre problème, mais au moins c'est agréable de pouvoir dire quel composant est cassé).
On dirait que c'est possible avec l'implémentation JAXB de Sun , même si je ne l'ai pas fait moi-même.
Pour une raison quelconque, je n'ai pas le temps de le savoir, cela a fonctionné pour moi
marshaller.setProperty(Marshaller.JAXB_ENCODING, "utf-8");
Au lieu d'utiliser "UTF-8"
ou "Unicode"
Je suggère que vous les essayiez, et comme @Javatar a dit , vérifiez-les en train de déposer pour classer en utilisant:
marshaller.marshal(shipOrder, new File("<test_file_path>"));
et en l'ouvrant avec un éditeur de texte décent comme notepad ++
Après avoir essayé toutes les solutions ci-dessus, est finalement arrivé à la conclusion.
votre logique de marshaling via le gestionnaire d’échappement personnalisé.
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();
Et le gestionnaire d’évasion personnalisé est le suivant:
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);
}
}
}
Le moyen le plus simple, lors de l'utilisation de l'implémentation Marshaller de Sun, consiste à fournir votre propre implémentation de CharacterEscapeEncoder qui n'échappe à rien.
Marshaller m = jcb.createMarshaller();
m.setProperty(
"com.Sun.xml.bind.marshaller.CharacterEscapeHandler",
new NullCharacterEscapeHandler());
Avec
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 );
}
}
intéressant mais avec des chaînes, vous pouvez essayer
Marshaller marshaller = jaxbContext.createMarshaller();
StringWriter sw = new StringWriter();
marshaller.marshal(data, sw);
sw.toString();
au moins pour moi cela n'échappe pas aux citations
Je déconseille d'utiliser CharacterEscapeHandler
pour les raisons mentionnées ci-dessus (c'est une classe interne). Au lieu de cela, vous pouvez utiliser Woodstox et fournir votre propre EscapingWriterFactory
à un XMLStreamWriter
. Quelque chose comme:
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 exemple de comment écrire une EscapingWriter
peut être vu dans CharacterEscapingTest .