[jOOQ/jOOQ#11341] intoXML() throws NPE exception

This includes:
- [jOOQ/jOOQ#11634] XMLFormat.quoteNested() is not being considered in Result.intoXML()
This commit is contained in:
Lukas Eder 2021-03-12 17:23:04 +01:00
parent aa0bbc2641
commit 50b7ba5e76
5 changed files with 106 additions and 22 deletions

View File

@ -219,6 +219,8 @@ abstract class AbstractFormattable implements Formattable, Serializable {
formatCSV(writer, new CSVFormat().header(header).delimiter(delimiter).nullString(nullString));
}
abstract JSONFormat defaultJSONFormat();
@Override
public final String formatJSON() {
StringWriter writer = new StringWriter();
@ -245,12 +247,14 @@ abstract class AbstractFormattable implements Formattable, Serializable {
@Override
public final void formatJSON(Writer writer) {
formatJSON(writer, null);
formatJSON(writer, defaultJSONFormat());
}
abstract XMLFormat defaultXMLFormat();
@Override
public final String formatXML() {
return formatXML((XMLFormat) null);
return formatXML(defaultXMLFormat());
}
@Override
@ -262,7 +266,7 @@ abstract class AbstractFormattable implements Formattable, Serializable {
@Override
public final void formatXML(OutputStream stream) {
formatXML(stream, null);
formatXML(stream, defaultXMLFormat());
}
@Override
@ -272,7 +276,7 @@ abstract class AbstractFormattable implements Formattable, Serializable {
@Override
public final void formatXML(Writer writer) {
formatXML(writer, null);
formatXML(writer, defaultXMLFormat());
}
@Override
@ -342,11 +346,11 @@ abstract class AbstractFormattable implements Formattable, Serializable {
@Override
public final Document intoXML() {
return intoXML((XMLFormat) null);
return intoXML(defaultXMLFormat());
}
@Override
public final <H extends ContentHandler> H intoXML(H handler) throws SAXException {
return intoXML(handler, null);
return intoXML(handler, defaultXMLFormat());
}
}

View File

@ -1050,10 +1050,12 @@ abstract class AbstractRecord extends AbstractStore implements Record {
}
@Override
public final void formatJSON(Writer writer, JSONFormat format) {
if (format == null)
format = JSONFormat.DEFAULT_FOR_RECORDS;
final JSONFormat defaultJSONFormat() {
return JSONFormat.DEFAULT_FOR_RECORDS;
}
@Override
public final void formatJSON(Writer writer, JSONFormat format) {
if (format.header())
log.debug("JSONFormat.header currently not supported for Record.formatJSON()");
@ -1075,10 +1077,12 @@ abstract class AbstractRecord extends AbstractStore implements Record {
}
@Override
public final void formatXML(Writer writer, XMLFormat format) {
if (format == null)
format = XMLFormat.DEFAULT_FOR_RECORDS;
final XMLFormat defaultXMLFormat() {
return XMLFormat.DEFAULT_FOR_RECORDS;
}
@Override
public final void formatXML(Writer writer, XMLFormat format) {
if (format.header())
log.debug("XMLFormat.header currently not supported for Record.formatXML()");

View File

@ -49,6 +49,7 @@ import static org.jooq.tools.StringUtils.abbreviate;
import static org.jooq.tools.StringUtils.leftPad;
import static org.jooq.tools.StringUtils.rightPad;
import java.io.StringReader;
import java.io.Writer;
import java.sql.Date;
import java.sql.Timestamp;
@ -99,11 +100,15 @@ import org.jooq.tools.StringUtils;
import org.jooq.tools.json.JSONValue;
import org.w3c.dom.Document;
import org.w3c.dom.DocumentFragment;
import org.w3c.dom.Element;
import org.w3c.dom.NodeList;
import org.xml.sax.Attributes;
import org.xml.sax.ContentHandler;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;
import org.xml.sax.helpers.AttributesImpl;
import org.xml.sax.helpers.DefaultHandler;
/**
* @author Lukas Eder
@ -611,10 +616,12 @@ abstract class AbstractResult<R extends Record> extends AbstractFormattable impl
}
@Override
public final void formatJSON(Writer writer, JSONFormat format) {
if (format == null)
format = JSONFormat.DEFAULT_FOR_RESULTS;
final JSONFormat defaultJSONFormat() {
return JSONFormat.DEFAULT_FOR_RESULTS;
}
@Override
public final void formatJSON(Writer writer, JSONFormat format) {
try {
String separator;
int recordLevel = format.header() ? 2 : 1;
@ -857,10 +864,12 @@ abstract class AbstractResult<R extends Record> extends AbstractFormattable impl
}
@Override
public final void formatXML(Writer writer, XMLFormat format) {
if (format == null)
format = XMLFormat.DEFAULT_FOR_RESULTS;
final XMLFormat defaultXMLFormat() {
return XMLFormat.DEFAULT_FOR_RESULTS;
}
@Override
public final void formatXML(Writer writer, XMLFormat format) {
String newline = format.newline();
int recordLevel = format.header() ? 2 : 1;
@ -1289,9 +1298,11 @@ abstract class AbstractResult<R extends Record> extends AbstractFormattable impl
eValue.setAttribute("field", field.getName());
eRecord.appendChild(eValue);
if (value != null) {
eValue.setTextContent(format0(value, false, false));
}
if (value != null)
if (value instanceof XML && !format.quoteNested())
eValue.appendChild(createContent(builder, document, ((XML) value).data()));
else
eValue.setTextContent(format0(value, false, false));
}
}
@ -1302,6 +1313,61 @@ abstract class AbstractResult<R extends Record> extends AbstractFormattable impl
}
}
// Taken from JOOX Util.createContent()
static final DocumentFragment createContent(DocumentBuilder builder, Document doc, String text) {
// [#150] Text might hold XML content, which can be leniently identified by the presence
// of either < or & characters (other entities, like >, ", ' are not stricly XML content)
if (text != null && (text.contains("<") || text.contains("&"))) {
// [#162] Prevent log output
builder.setErrorHandler(new DefaultHandler());
try {
// [#128] Trimming will get rid of leading and trailing whitespace, which would
// otherwise cause a HIERARCHY_REQUEST_ERR raised by the parser
text = text.trim();
// There is a processing instruction. We can safely assume
// valid XML and parse it as such
if (text.startsWith("<?xml")) {
Document parsed = builder.parse(new InputSource(new StringReader(text)));
DocumentFragment fragment = parsed.createDocumentFragment();
fragment.appendChild(parsed.getDocumentElement());
return (DocumentFragment) doc.importNode(fragment, true);
}
// Any XML document fragment. To be on the safe side, fragments
// are wrapped in a dummy root node
else {
String wrapped = "<dummy>" + text + "</dummy>";
Document parsed = builder.parse(new InputSource(new StringReader(wrapped)));
DocumentFragment fragment = parsed.createDocumentFragment();
NodeList children = parsed.getDocumentElement().getChildNodes();
// appendChild removes children also from NodeList!
while (children.getLength() > 0) {
fragment.appendChild(children.item(0));
}
return (DocumentFragment) doc.importNode(fragment, true);
}
}
// This does not occur
catch (java.io.IOException ignore) {}
// The XML content is invalid
catch (SAXException ignore) {}
}
// Plain text or invalid XML
return null;
}
@Override
public final <H extends ContentHandler> H intoXML(H handler, XMLFormat format) throws SAXException {
Attributes empty = new AttributesImpl();

View File

@ -412,6 +412,16 @@ package org.jooq.impl;

View File

@ -65,7 +65,7 @@ final class XMLElement extends AbstractField<XML> {
private final QueryPartList<Field<?>> content;
XMLElement(Name elementName, XMLAttributes attributes, Collection<? extends Field<?>> content) {
super(N_XMLCONCAT, SQLDataType.XML);
super(N_XMLELEMENT, SQLDataType.XML);
this.elementName = elementName;
this.attributes = attributes;