[jOOQ/jOOQ#19147] Result::formatXML should offer a way to configure NULL

encoding
This commit is contained in:
Lukas Eder 2025-10-06 16:39:22 +02:00
parent b91b735808
commit 98d66c48ed
2 changed files with 110 additions and 35 deletions

View File

@ -64,6 +64,7 @@ public final class XMLFormat {
boolean header;
RecordFormat recordFormat;
boolean quoteNested;
NullFormat nullFormat;
public XMLFormat() {
this(
@ -76,7 +77,8 @@ public final class XMLFormat {
null,
true,
RecordFormat.VALUE_ELEMENTS_WITH_FIELD_ATTRIBUTE,
false
false,
NullFormat.EMPTY_ELEMENT
);
}
@ -90,7 +92,8 @@ public final class XMLFormat {
String[] indented,
boolean header,
RecordFormat recordFormat,
boolean quoteNested
boolean quoteNested,
NullFormat nullFormat
) {
this.mutable = mutable;
this.xmlns = xmlns;
@ -107,6 +110,7 @@ public final class XMLFormat {
this.header = header;
this.recordFormat = recordFormat;
this.quoteNested = quoteNested;
this.nullFormat = nullFormat;
}
/**
@ -132,7 +136,8 @@ public final class XMLFormat {
indented,
header,
recordFormat,
quoteNested
quoteNested,
nullFormat
);
else
return this;
@ -158,7 +163,8 @@ public final class XMLFormat {
indented,
header,
recordFormat,
quoteNested
quoteNested,
nullFormat
);
}
@ -189,7 +195,8 @@ public final class XMLFormat {
null,
header,
recordFormat,
quoteNested
quoteNested,
nullFormat
);
}
@ -220,7 +227,8 @@ public final class XMLFormat {
indented,
header,
recordFormat,
quoteNested
quoteNested,
nullFormat
);
}
@ -252,7 +260,8 @@ public final class XMLFormat {
null,
header,
recordFormat,
quoteNested
quoteNested,
nullFormat
);
}
@ -283,7 +292,8 @@ public final class XMLFormat {
null,
header,
recordFormat,
quoteNested
quoteNested,
nullFormat
);
}
@ -335,7 +345,8 @@ public final class XMLFormat {
indented,
newHeader,
recordFormat,
quoteNested
quoteNested,
nullFormat
);
}
@ -367,7 +378,8 @@ public final class XMLFormat {
indented,
header,
newRecordFormat,
quoteNested
quoteNested,
nullFormat
);
}
@ -401,7 +413,8 @@ public final class XMLFormat {
indented,
header,
recordFormat,
newQuoteNested
newQuoteNested,
nullFormat
);
}
@ -413,6 +426,40 @@ public final class XMLFormat {
return quoteNested;
}
/**
* Whether nested {@link XML} content should be quoted like a string, or
* nested into XML formatted output.
*/
@NotNull
public final XMLFormat nullFormat(NullFormat newNullFormat) {
if (mutable) {
nullFormat = newNullFormat;
return this;
}
else
return new XMLFormat(
mutable,
xmlns,
format,
newline,
globalIndent,
indent,
indented,
header,
recordFormat,
quoteNested,
newNullFormat
);
}
/**
* Whether nested {@link XML} content should be quoted like a string, or
* nested into XML formatted output.
*/
public final NullFormat nullFormat() {
return nullFormat;
}
/**
* The format of individual XML records.
*/
@ -433,4 +480,25 @@ public final class XMLFormat {
*/
COLUMN_NAME_ELEMENTS,
}
/**
* The format of a <code>null</code> value.
*/
public enum NullFormat {
/**
* A <code>null</code> value is represented by an empty tag.
*/
EMPTY_ELEMENT,
/**
* A <code>null</code> value is represented by an absent tag.
*/
ABSENT_ELEMENT,
/**
* A <code>null</code> value is represented by a <code>xsi:nil="true"</code> attribute.
*/
XSI_NIL
}
}

View File

@ -41,6 +41,8 @@ import static java.lang.Math.max;
import static java.lang.Math.min;
import static java.util.stream.Collectors.joining;
import static org.jooq.JSONFormat.NullFormat.ABSENT_ON_NULL;
import static org.jooq.XMLFormat.NullFormat.ABSENT_ELEMENT;
import static org.jooq.XMLFormat.NullFormat.XSI_NIL;
import static org.jooq.XMLFormat.RecordFormat.COLUMN_NAME_ELEMENTS;
import static org.jooq.XMLFormat.RecordFormat.VALUE_ELEMENTS_WITH_FIELD_ATTRIBUTE;
import static org.jooq.conf.SettingsTools.renderLocale;
@ -892,32 +894,37 @@ abstract class AbstractResult<R extends Record> extends AbstractFormattable impl
? escapeXML(fields.field(index).getName())
: "value";
writer.append("<" + tag);
if (format.recordFormat() == VALUE_ELEMENTS_WITH_FIELD_ATTRIBUTE) {
writer.append(" field=\"");
writer.append(escapeXML(fields.field(index).getName()));
writer.append("\"");
}
if (value == null) {
writer.append("/>");
}
else {
writer.append(">");
if (value instanceof Formattable f) {
writer.append(newline).append(format.indentString(recordLevel + 2));
int previous = format.globalIndent();
f.formatXML(writer, format.globalIndent(format.globalIndent() + format.indent() * (recordLevel + 2)));
format.globalIndent(previous);
writer.append(newline).append(format.indentString(recordLevel + 1));
if (value != null || format.nullFormat() != ABSENT_ELEMENT) {
writer.append("<" + tag);
if (format.recordFormat() == VALUE_ELEMENTS_WITH_FIELD_ATTRIBUTE) {
writer.append(" field=\"");
writer.append(escapeXML(fields.field(index).getName()));
writer.append("\"");
}
else if (value instanceof XML && !format.quoteNested())
writer.append(((XML) value).data());
else
writer.append(escapeXML(format0(value, false, false)));
writer.append("</" + tag + ">");
if (value == null) {
if (format.nullFormat() == XSI_NIL)
writer.append(" xsi:nil=\"true\"");
writer.append("/>");
}
else {
writer.append(">");
if (value instanceof Formattable f) {
writer.append(newline).append(format.indentString(recordLevel + 2));
int previous = format.globalIndent();
f.formatXML(writer, format.globalIndent(format.globalIndent() + format.indent() * (recordLevel + 2)));
format.globalIndent(previous);
writer.append(newline).append(format.indentString(recordLevel + 1));
}
else if (value instanceof XML && !format.quoteNested())
writer.append(((XML) value).data());
else
writer.append(escapeXML(format0(value, false, false)));
writer.append("</" + tag + ">");
}
}
}