Skip to content

Commit 656d2b4

Browse files
cowtowncoderalex-bel-apica
authored andcommitted
# Conflicts: # release-notes/CREDITS-2.x # release-notes/VERSION-2.x
1 parent 98e0dfe commit 656d2b4

File tree

7 files changed

+179
-20
lines changed

7 files changed

+179
-20
lines changed

src/main/java/com/fasterxml/jackson/dataformat/xml/XmlMapper.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -160,6 +160,11 @@ public XmlMapper(XmlFactory xmlFactory, JacksonXmlModule module)
160160
// 21-Jun-2017, tatu: Seems like there are many cases in XML where ability to coerce empty
161161
// String into `null` (where it otherwise is an error) is very useful.
162162
enable(DeserializationFeature.ACCEPT_EMPTY_STRING_AS_NULL_OBJECT);
163+
164+
// 13-May-2020, tatu: [dataformat-xml#377] Need to ensure we will keep XML-specific
165+
// Base64 default as "MIME" (not MIME-NO-LINEFEEDS), to preserve pre-2.12
166+
// behavior
167+
setBase64Variant(Base64Variants.MIME);
163168
}
164169

165170
/**

src/main/java/com/fasterxml/jackson/dataformat/xml/XmlPrettyPrinter.java

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -84,12 +84,13 @@ public void writeLeafElement(XMLStreamWriter2 sw,
8484

8585
// binary element
8686
public void writeLeafElement(XMLStreamWriter2 sw,
87-
String nsURI, String localName,
88-
byte[] data, int offset, int len)
87+
String nsURI, String localName,
88+
org.codehaus.stax2.typed.Base64Variant base64variant,
89+
byte[] data, int offset, int len)
8990
throws XMLStreamException;
9091

9192
// empty element to represent null
9293
public void writeLeafNullElement(XMLStreamWriter2 sw,
93-
String nsURI, String localName)
94+
String nsURI, String localName)
9495
throws XMLStreamException;
9596
}

src/main/java/com/fasterxml/jackson/dataformat/xml/ser/ToXmlGenerator.java

Lines changed: 16 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -834,22 +834,24 @@ public void writeBinary(Base64Variant b64variant,
834834
if (_nextName == null) {
835835
handleMissingName();
836836
}
837+
final org.codehaus.stax2.typed.Base64Variant stax2base64v = StaxUtil.toStax2Base64Variant(b64variant);
837838
try {
838839
if (_nextIsAttribute) {
839840
// Stax2 API only has 'full buffer' write method:
840841
byte[] fullBuffer = toFullBuffer(data, offset, len);
841-
_xmlWriter.writeBinaryAttribute("", _nextName.getNamespaceURI(), _nextName.getLocalPart(), fullBuffer);
842+
_xmlWriter.writeBinaryAttribute(stax2base64v,
843+
"", _nextName.getNamespaceURI(), _nextName.getLocalPart(), fullBuffer);
842844
} else if (checkNextIsUnwrapped()) {
843845
// should we consider pretty-printing or not?
844-
_xmlWriter.writeBinary(data, offset, len);
846+
_xmlWriter.writeBinary(stax2base64v, data, offset, len);
845847
} else {
846848
if (_xmlPrettyPrinter != null) {
847849
_xmlPrettyPrinter.writeLeafElement(_xmlWriter,
848850
_nextName.getNamespaceURI(), _nextName.getLocalPart(),
849-
data, offset, len);
851+
stax2base64v, data, offset, len);
850852
} else {
851853
_xmlWriter.writeStartElement(_nextName.getNamespaceURI(), _nextName.getLocalPart());
852-
_xmlWriter.writeBinary(data, offset, len);
854+
_xmlWriter.writeBinary(stax2base64v, data, offset, len);
853855
_xmlWriter.writeEndElement();
854856
}
855857
}
@@ -869,23 +871,25 @@ public int writeBinary(Base64Variant b64variant, InputStream data, int dataLengt
869871
if (_nextName == null) {
870872
handleMissingName();
871873
}
874+
final org.codehaus.stax2.typed.Base64Variant stax2base64v = StaxUtil.toStax2Base64Variant(b64variant);
872875
try {
873876
if (_nextIsAttribute) {
874877
// Stax2 API only has 'full buffer' write method:
875878
byte[] fullBuffer = toFullBuffer(data, dataLength);
876-
_xmlWriter.writeBinaryAttribute("", _nextName.getNamespaceURI(), _nextName.getLocalPart(), fullBuffer);
879+
_xmlWriter.writeBinaryAttribute(stax2base64v,
880+
"", _nextName.getNamespaceURI(), _nextName.getLocalPart(), fullBuffer);
877881
} else if (checkNextIsUnwrapped()) {
878882
// should we consider pretty-printing or not?
879-
writeStreamAsBinary(data, dataLength);
883+
writeStreamAsBinary(stax2base64v, data, dataLength);
880884

881885
} else {
882886
if (_xmlPrettyPrinter != null) {
883887
_xmlPrettyPrinter.writeLeafElement(_xmlWriter,
884888
_nextName.getNamespaceURI(), _nextName.getLocalPart(),
885-
toFullBuffer(data, dataLength), 0, dataLength);
889+
stax2base64v, toFullBuffer(data, dataLength), 0, dataLength);
886890
} else {
887891
_xmlWriter.writeStartElement(_nextName.getNamespaceURI(), _nextName.getLocalPart());
888-
writeStreamAsBinary(data, dataLength);
892+
writeStreamAsBinary(stax2base64v, data, dataLength);
889893
_xmlWriter.writeEndElement();
890894
}
891895
}
@@ -896,7 +900,8 @@ public int writeBinary(Base64Variant b64variant, InputStream data, int dataLengt
896900
return dataLength;
897901
}
898902

899-
private void writeStreamAsBinary(InputStream data, int len) throws IOException, XMLStreamException
903+
private void writeStreamAsBinary(org.codehaus.stax2.typed.Base64Variant stax2base64v,
904+
InputStream data, int len) throws IOException, XMLStreamException
900905
{
901906
// base64 encodes up to 3 bytes into a 4 bytes string
902907
byte[] tmp = new byte[3];
@@ -907,7 +912,7 @@ private void writeStreamAsBinary(InputStream data, int len) throws IOException,
907912
len -= read;
908913
if(offset == 3) {
909914
offset = 0;
910-
_xmlWriter.writeBinary(tmp, 0, 3);
915+
_xmlWriter.writeBinary(stax2base64v, tmp, 0, 3);
911916
}
912917
if (len == 0) {
913918
break;
@@ -916,11 +921,10 @@ private void writeStreamAsBinary(InputStream data, int len) throws IOException,
916921

917922
// we still have < 3 bytes in the buffer
918923
if(offset > 0) {
919-
_xmlWriter.writeBinary(tmp, 0, offset);
924+
_xmlWriter.writeBinary(stax2base64v, tmp, 0, offset);
920925
}
921926
}
922927

923-
924928
private byte[] toFullBuffer(byte[] data, int offset, int len)
925929
{
926930
// might already be ok:

src/main/java/com/fasterxml/jackson/dataformat/xml/util/DefaultXmlPrettyPrinter.java

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -396,24 +396,26 @@ public void writeLeafElement(XMLStreamWriter2 sw,
396396
_justHadStartElement = false;
397397
}
398398

399+
// method definition changed in 2.12
399400
@Override
400401
public void writeLeafElement(XMLStreamWriter2 sw,
401-
String nsURI, String localName,
402-
byte[] data, int offset, int len)
402+
String nsURI, String localName,
403+
org.codehaus.stax2.typed.Base64Variant base64variant,
404+
byte[] data, int offset, int len)
403405
throws XMLStreamException
404406
{
405407
if (!_objectIndenter.isInline()) {
406408
_objectIndenter.writeIndentation(sw, _nesting);
407409
}
408410
sw.writeStartElement(nsURI, localName);
409-
sw.writeBinary(data, offset, len);
411+
sw.writeBinary(base64variant, data, offset, len);
410412
sw.writeEndElement();
411413
_justHadStartElement = false;
412414
}
413415

414416
@Override
415417
public void writeLeafNullElement(XMLStreamWriter2 sw,
416-
String nsURI, String localName)
418+
String nsURI, String localName)
417419
throws XMLStreamException
418420
{
419421
if (!_objectIndenter.isInline()) {

src/main/java/com/fasterxml/jackson/dataformat/xml/util/StaxUtil.java

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,13 @@
11
package com.fasterxml.jackson.dataformat.xml.util;
22

33
import java.io.IOException;
4+
import java.util.HashMap;
5+
import java.util.Map;
46

57
import javax.xml.stream.*;
68

9+
import com.fasterxml.jackson.core.Base64Variant;
10+
import com.fasterxml.jackson.core.Base64Variants;
711
import com.fasterxml.jackson.core.JsonGenerationException;
812
import com.fasterxml.jackson.core.JsonGenerator;
913
import com.fasterxml.jackson.core.JsonParseException;
@@ -109,4 +113,48 @@ public static String sanitizeXmlTypeName(String name)
109113
}
110114
return sb.toString();
111115
}
116+
117+
/**
118+
* Helper method used to "convert" Jackson's {@link Base64Variant} into corresponding
119+
* Stax2 equivalent, to try to allow Jackson-style configuration for XML output as well.
120+
*
121+
* @param j64b Jackson base64 variant to find match for
122+
*
123+
* @return Stax2 Base64 variant that most closely resembles Jackson canonical Base64 variant
124+
* passed in as argument
125+
*
126+
* @since 2.12
127+
*/
128+
public static org.codehaus.stax2.typed.Base64Variant toStax2Base64Variant(Base64Variant j64b) {
129+
return Base64Mapper.instance.map(j64b);
130+
}
131+
132+
private static class Base64Mapper {
133+
public final static Base64Mapper instance = new Base64Mapper();
134+
135+
private final Map<String, org.codehaus.stax2.typed.Base64Variant> j2stax2
136+
= new HashMap<>();
137+
{
138+
j2stax2.put(Base64Variants.MIME.getName(), org.codehaus.stax2.typed.Base64Variants.MIME);
139+
j2stax2.put(Base64Variants.MIME_NO_LINEFEEDS.getName(),
140+
org.codehaus.stax2.typed.Base64Variants.MIME_NO_LINEFEEDS);
141+
j2stax2.put(Base64Variants.MODIFIED_FOR_URL.getName(),
142+
org.codehaus.stax2.typed.Base64Variants.MODIFIED_FOR_URL);
143+
j2stax2.put(Base64Variants.PEM.getName(), org.codehaus.stax2.typed.Base64Variants.PEM);
144+
}
145+
146+
private Base64Mapper() {
147+
}
148+
149+
public org.codehaus.stax2.typed.Base64Variant map(Base64Variant j64b) {
150+
org.codehaus.stax2.typed.Base64Variant result = j2stax2.get(j64b.getName());
151+
if (result == null) {
152+
// 13-May-2020, tatu: in unexpected case of no match, default to what Stax2
153+
// considers default, not Jackson: this for backwards compatibility with
154+
// Jackson 2.11 and earlier
155+
result = org.codehaus.stax2.typed.Base64Variants.getDefaultVariant();
156+
}
157+
return result;
158+
}
159+
}
112160
}
Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,99 @@
1+
package com.fasterxml.jackson.dataformat.xml.ser;
2+
3+
import com.fasterxml.jackson.core.Base64Variant;
4+
import com.fasterxml.jackson.core.Base64Variants;
5+
import com.fasterxml.jackson.databind.ObjectReader;
6+
import com.fasterxml.jackson.databind.ObjectWriter;
7+
import com.fasterxml.jackson.dataformat.xml.XmlMapper;
8+
import com.fasterxml.jackson.dataformat.xml.XmlTestBase;
9+
10+
import static org.junit.Assert.*;
11+
12+
public class Base64VariantWriteTest extends XmlTestBase
13+
{
14+
private static final String EOL = System.lineSeparator();
15+
16+
public static class BinaryValue {
17+
public byte[] value;
18+
19+
protected BinaryValue() { }
20+
public BinaryValue(byte[] v) {
21+
value = v;
22+
}
23+
}
24+
25+
private final byte[] BINARY_DATA;
26+
{
27+
try {
28+
BINARY_DATA = "abcdefghijklmnopqrstuvwxyz1234567890abcdefghijklmnopqrstuvwxyz1234567890X".getBytes("UTF-8");
29+
} catch (Exception e) {
30+
throw new Error(e);
31+
}
32+
}
33+
34+
private final static String XML_MIME_NO_LINEFEEDS =
35+
"YWJjZGVmZ2hpamtsbW5vcHFyc3R1dnd4eXoxMjM0NTY3ODkwYWJjZGVmZ2hpamtsbW5vcHFyc3R1dnd4eXoxMjM0NTY3ODkwWA==";
36+
37+
private final static String XML_MIME =
38+
"YWJjZGVmZ2hpamtsbW5vcHFyc3R1dnd4eXoxMjM0NTY3ODkwYWJjZGVmZ2hpamtsbW5vcHFyc3R1\n"
39+
+"dnd4eXoxMjM0NTY3ODkwWA==";
40+
private final static String XML_MOD_FOR_URL =
41+
"YWJjZGVmZ2hpamtsbW5vcHFyc3R1dnd4eXoxMjM0NTY3ODkwYWJjZGVmZ2hpamtsbW5vcHFyc3R1dnd4eXoxMjM0NTY3ODkwWA";
42+
private final static String XML_PEM =
43+
"YWJjZGVmZ2hpamtsbW5vcHFyc3R1dnd4eXoxMjM0NTY3ODkwYWJjZGVmZ2hpamts\n"
44+
+"bW5vcHFyc3R1dnd4eXoxMjM0NTY3ODkwWA==";
45+
46+
private final XmlMapper MAPPER = newMapper();
47+
48+
public void testBinaryVariantsCompact() throws Exception
49+
{
50+
_testBinaryVariants(Base64Variants.MIME, XML_MIME, false);
51+
52+
_testBinaryVariants(Base64Variants.MIME_NO_LINEFEEDS, XML_MIME_NO_LINEFEEDS, false);
53+
_testBinaryVariants(Base64Variants.MODIFIED_FOR_URL, XML_MOD_FOR_URL, false);
54+
_testBinaryVariants(Base64Variants.PEM, XML_PEM, false);
55+
56+
// default pre-2.12 was "MIME", despite Jackson/json default of "MIME_NO_LINEFEEDS,
57+
// so kept the same for 2.12 by changing XMLMapper defaults
58+
_testBinaryVariants(null, XML_MIME, false);
59+
}
60+
61+
public void testBinaryVariantsPretty() throws Exception
62+
{
63+
_testBinaryVariants(Base64Variants.MIME, XML_MIME, true);
64+
65+
_testBinaryVariants(Base64Variants.MIME_NO_LINEFEEDS, XML_MIME_NO_LINEFEEDS, true);
66+
_testBinaryVariants(Base64Variants.MODIFIED_FOR_URL, XML_MOD_FOR_URL, true);
67+
_testBinaryVariants(Base64Variants.PEM, XML_PEM, true);
68+
69+
// default pre-2.12 was "MIME", despite Jackson/json default of "MIME_NO_LINEFEEDS,
70+
// so kept the same for 2.12 by changing XMLMapper defaults
71+
_testBinaryVariants(null, XML_MIME, true);
72+
}
73+
74+
private void _testBinaryVariants(Base64Variant b64v, String expEncoded,
75+
boolean indent) throws Exception
76+
{
77+
ObjectWriter w = MAPPER.writer();
78+
if (indent) {
79+
w = w.withDefaultPrettyPrinter();
80+
}
81+
ObjectReader r = MAPPER.readerFor(BinaryValue.class);
82+
if (b64v != null) {
83+
w = w.with(b64v);
84+
r = r.with(b64v);
85+
}
86+
final String EXP = indent ?
87+
"<BinaryValue>" + EOL + " <value>"+expEncoded+"</value>" + EOL + "</BinaryValue>" :
88+
"<BinaryValue><value>"+expEncoded+"</value></BinaryValue>";
89+
final String xml = w.writeValueAsString(new BinaryValue(BINARY_DATA)).trim();
90+
91+
//System.err.println("EXP:\n"+EXP+"\nACT:\n"+xml+"\n");
92+
93+
assertEquals(EXP, xml);
94+
95+
// and read back just for shirts & goggles
96+
BinaryValue result = r.readValue(EXP);
97+
assertArrayEquals(BINARY_DATA, result.value);
98+
}
99+
}

src/test/java/com/fasterxml/jackson/dataformat/xml/ser/TestBinaryStreamToXMLSerialization.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111
*/
1212
public class TestBinaryStreamToXMLSerialization extends XmlTestBase
1313
{
14-
private final XmlMapper MAPPER = new XmlMapper();
14+
private final XmlMapper MAPPER = newMapper();
1515

1616
public void testWith0Bytes() throws Exception
1717
{

0 commit comments

Comments
 (0)