Skip to content

Commit

Permalink
Fix #245
Browse files Browse the repository at this point in the history
  • Loading branch information
cowtowncoder committed Jun 22, 2017
1 parent 2648916 commit 95e4b9a
Show file tree
Hide file tree
Showing 8 changed files with 103 additions and 42 deletions.
2 changes: 2 additions & 0 deletions release-notes/VERSION
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ Project: jackson-dataformat-xml

#232: Implement `writeRawValue` in `ToXmlGenerator`
(contributed by Yury V)
#245: Default `DeserializationFeature.ACCEPT_EMPTY_STRING_AS_NULL_OBJECT`
to "enabled" for `XmlMapper`

2.8.9 (12-Jun-2017)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -500,7 +500,7 @@ public FromXmlParser createParser(XMLStreamReader sr) throws IOException

// false -> not managed
FromXmlParser xp = new FromXmlParser(_createContext(sr, false),
_generatorFeatures, _xmlGeneratorFeatures, _objectCodec, sr);
_parserFeatures, _xmlParserFeatures, _objectCodec, sr);
if (_cfgNameForTextElement != null) {
xp.setXMLTextElementName(_cfgNameForTextElement);
}
Expand All @@ -525,7 +525,7 @@ public ToXmlGenerator createGenerator(XMLStreamWriter sw) throws IOException
return new ToXmlGenerator(ctxt, _generatorFeatures, _xmlGeneratorFeatures,
_objectCodec, sw);
}

/*
/**********************************************************
/* Internal factory method overrides
Expand All @@ -542,7 +542,7 @@ protected FromXmlParser _createParser(InputStream in, IOContext ctxt) throws IOE
} catch (XMLStreamException e) {
return StaxUtil.throwXmlAsIOException(e);
}
FromXmlParser xp = new FromXmlParser(ctxt, _generatorFeatures, _xmlGeneratorFeatures,
FromXmlParser xp = new FromXmlParser(ctxt, _parserFeatures, _xmlParserFeatures,
_objectCodec, sr);
if (_cfgNameForTextElement != null) {
xp.setXMLTextElementName(_cfgNameForTextElement);
Expand All @@ -560,7 +560,7 @@ protected FromXmlParser _createParser(Reader r, IOContext ctxt) throws IOExcepti
} catch (XMLStreamException e) {
return StaxUtil.throwXmlAsIOException(e);
}
FromXmlParser xp = new FromXmlParser(ctxt, _generatorFeatures, _xmlGeneratorFeatures,
FromXmlParser xp = new FromXmlParser(ctxt, _parserFeatures, _xmlParserFeatures,
_objectCodec, sr);
if (_cfgNameForTextElement != null) {
xp.setXMLTextElementName(_cfgNameForTextElement);
Expand All @@ -581,7 +581,7 @@ protected FromXmlParser _createParser(char[] data, int offset, int len, IOContex
} catch (XMLStreamException e) {
return StaxUtil.throwXmlAsIOException(e);
}
FromXmlParser xp = new FromXmlParser(ctxt, _generatorFeatures, _xmlGeneratorFeatures,
FromXmlParser xp = new FromXmlParser(ctxt, _parserFeatures, _xmlParserFeatures,
_objectCodec, sr);
if (_cfgNameForTextElement != null) {
xp.setXMLTextElementName(_cfgNameForTextElement);
Expand All @@ -599,7 +599,7 @@ protected FromXmlParser _createParser(byte[] data, int offset, int len, IOContex
} catch (XMLStreamException e) {
return StaxUtil.throwXmlAsIOException(e);
}
FromXmlParser xp = new FromXmlParser(ctxt, _generatorFeatures, _xmlGeneratorFeatures,
FromXmlParser xp = new FromXmlParser(ctxt, _parserFeatures, _xmlParserFeatures,
_objectCodec, sr);
if (_cfgNameForTextElement != null) {
xp.setXMLTextElementName(_cfgNameForTextElement);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,9 @@ public XmlMapper(XmlFactory xmlFactory, JacksonXmlModule module)
}
// 19-May-2015, tatu: Must ensure we use XML-specific indenter
_serializationConfig = _serializationConfig.withDefaultPrettyPrinter(DEFAULT_XML_PRETTY_PRINTER);
// 21-Jun-2017, tatu: Seems like there are many cases in XML where ability to coerce empty
// String into `null` (where it otherwise is an error) is very useful.
enable(DeserializationFeature.ACCEPT_EMPTY_STRING_AS_NULL_OBJECT);
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,10 +31,21 @@ public class FromXmlParser

/**
* Enumeration that defines all togglable features for XML parsers.
* None defined so far (2.6), so just a placeholder.
*/
public enum Feature implements FormatFeature
{
/**
* Feature that indicates whether XML Empty elements (ones where there are
* no separate start and end tages, but just one tag that ends with "/>")
* are exposed as {@link JsonToken#VALUE_NULL}) or not. If they are not
* returned as `null` tokens, they will be returned as {@link JsonToken#VALUE_STRING}
* tokens with textual value of "" (empty String).
*<p>
* Default setting is `true` for backwards compatibility.
*
* @since 2.9
*/
EMPTY_ELEMENT_AS_NULL(true)
;

final boolean _defaultState;
Expand Down Expand Up @@ -171,7 +182,8 @@ public FromXmlParser(IOContext ctxt, int genericParserFeatures, int xmlFeatures,
_parsingContext = XmlReadContext.createRootContext(-1, -1);
// and thereby start a scope
_nextToken = JsonToken.START_OBJECT;
_xmlTokens = new XmlTokenStream(xmlReader, ctxt.getSourceReference());
_xmlTokens = new XmlTokenStream(xmlReader, ctxt.getSourceReference(),
_formatFeatures);
}

@Override
Expand Down Expand Up @@ -215,11 +227,13 @@ public boolean requiresCustomCodec() {

public FromXmlParser enable(Feature f) {
_formatFeatures |= f.getMask();
_xmlTokens.setFormatFeatures(_formatFeatures);
return this;
}

public FromXmlParser disable(Feature f) {
_formatFeatures &= ~f.getMask();
_xmlTokens.setFormatFeatures(_formatFeatures);
return this;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,13 @@ public class XmlTokenStream
final protected XMLStreamReader2 _xmlReader;

final protected Object _sourceReference;

/**
* Bit flag composed of bits that indicate which
* {@link FromXmlParser.Feature}s
* are enabled.
*/
protected int _formatFeatures;

/*
/**********************************************************************
Expand Down Expand Up @@ -104,7 +111,8 @@ public class XmlTokenStream
/**********************************************************************
*/

public XmlTokenStream(XMLStreamReader xmlReader, Object sourceRef)
public XmlTokenStream(XMLStreamReader xmlReader, Object sourceRef,
int formatFeatures)
{
_sourceReference = sourceRef;
// Let's ensure we point to START_ELEMENT...
Expand All @@ -117,12 +125,20 @@ public XmlTokenStream(XMLStreamReader xmlReader, Object sourceRef)
_localName = _xmlReader.getLocalName();
_namespaceURI = _xmlReader.getNamespaceURI();
_attributeCount = _xmlReader.getAttributeCount();
_formatFeatures = formatFeatures;
}

public XMLStreamReader2 getXmlReader() {
return _xmlReader;
}

/**
* @since 2.9
*/
protected void setFormatFeatures(int f) {
_formatFeatures = f;
}

/*
/**********************************************************************
/* Public API
Expand Down Expand Up @@ -368,26 +384,33 @@ private final int _next() throws XMLStreamException
// START_ELEMENT...
return _initStartElement();
}

private final String _collectUntilTag() throws XMLStreamException
{
// 21-Jun-2017, tatu: Whether exposed as `null` or "" is now configurable...
if (_xmlReader.isEmptyElement()) {
_xmlReader.next();
return null;
if (FromXmlParser.Feature.EMPTY_ELEMENT_AS_NULL.enabledIn(_formatFeatures)) {
return null;
}
return "";
}

StringBuilder text = new StringBuilder();

String text = null;
while (true) {
switch (_xmlReader.next()) {
case XMLStreamConstants.START_ELEMENT:
case XMLStreamConstants.END_ELEMENT:
case XMLStreamConstants.END_DOCUMENT:
return text.toString();
return (text == null) ? "" : text;
// note: SPACE is ignorable (and seldom seen), not to be included
case XMLStreamConstants.CHARACTERS:
case XMLStreamConstants.CDATA:
text.append(_xmlReader.getText());
if (text == null) {
text = _xmlReader.getText();
} else {
text += _xmlReader.getText();
}
break;
default:
// any other type (proc instr, comment etc) is just ignored
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
import com.fasterxml.jackson.dataformat.xml.XmlMapper;
import com.fasterxml.jackson.dataformat.xml.XmlTestBase;

public class TestStringValues162 extends XmlTestBase
public class EmptyStringValueTest extends XmlTestBase
{
static class Name {
public String first;
Expand Down Expand Up @@ -58,24 +58,4 @@ public void testEmptyStringElement() throws Exception
assertEquals("", bean.text);
// assertNull(bean.text);
}

public void testStringsInList() throws Exception
{
Names input = new Names();
input.names.add(new Name("Bob", "Lee"));
input.names.add(new Name("", ""));
input.names.add(new Name("Sponge", "Bob"));
String xml = MAPPER.writerWithDefaultPrettyPrinter().writeValueAsString(input);

//System.err.println("XML:\n"+xml);

Names result = MAPPER.readValue(xml, Names.class);
assertNotNull(result);
assertNotNull(result.names);
assertEquals(3, result.names.size());
assertEquals("Bob", result.names.get(2).last);

// [dataformat-xml#162]: should get empty String, not null
assertEquals("", result.names.get(1).first);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,10 @@

import com.fasterxml.jackson.dataformat.xml.XmlMapper;
import com.fasterxml.jackson.dataformat.xml.XmlTestBase;
import com.fasterxml.jackson.dataformat.xml.deser.EmptyStringValueTest.Name;
import com.fasterxml.jackson.dataformat.xml.deser.EmptyStringValueTest.Names;

public class TestStringValues extends XmlTestBase
public class SimpleStringValuesTest extends XmlTestBase
{
protected static class Bean2
{
Expand All @@ -21,7 +23,7 @@ static class Issue167Bean {

/*
/**********************************************************
/* Unit tests
/* Tests, basic
/**********************************************************
*/

Expand All @@ -44,6 +46,12 @@ public void testMissingString() throws Exception
assertEquals(baseline.text, bean.text);
}

/*
/**********************************************************
/* Tests, with attributes
/**********************************************************
*/

public void testStringWithAttribute() throws Exception
{
// and then the money shot: with 'standard' attribute...
Expand Down Expand Up @@ -93,4 +101,30 @@ public void testEmptyElementToString() throws Exception
assertNotNull(result);
assertEquals("", result.d);
}

/*
/**********************************************************
/* Tests, Lists
/**********************************************************
*/

public void testStringsInList() throws Exception
{
Names input = new Names();
input.names.add(new Name("Bob", "Lee"));
input.names.add(new Name("", ""));
input.names.add(new Name("Sponge", "Bob"));
String xml = MAPPER.writerWithDefaultPrettyPrinter().writeValueAsString(input);

//System.err.println("XML:\n"+xml);

Names result = MAPPER.readValue(xml, Names.class);
assertNotNull(result);
assertNotNull(result.names);
assertEquals(3, result.names.size());
assertEquals("Bob", result.names.get(2).last);

// [dataformat-xml#162]: should get empty String, not null
assertEquals("", result.names.get(1).first);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
import javax.xml.stream.*;

import com.fasterxml.jackson.dataformat.xml.XmlTestBase;
import com.fasterxml.jackson.dataformat.xml.deser.FromXmlParser;
import com.fasterxml.jackson.dataformat.xml.deser.XmlTokenStream;

public class XmlTokenStreamTest extends XmlTestBase
Expand All @@ -15,7 +16,8 @@ public void testSimple() throws Exception
XMLStreamReader sr = XMLInputFactory.newInstance().createXMLStreamReader(new StringReader(XML));
// must point to START_ELEMENT, so:
sr.nextTag();
XmlTokenStream tokens = new XmlTokenStream(sr, XML);
XmlTokenStream tokens = new XmlTokenStream(sr, XML,
FromXmlParser.Feature.collectDefaults());
assertEquals(XmlTokenStream.XML_START_ELEMENT, tokens.getCurrentToken());
assertEquals("root", tokens.getLocalName());
assertEquals(XmlTokenStream.XML_START_ELEMENT, tokens.next());
Expand All @@ -37,7 +39,8 @@ public void testRootAttributes() throws Exception
XMLStreamReader sr = XMLInputFactory.newInstance().createXMLStreamReader(new StringReader(XML));
// must point to START_ELEMENT, so:
sr.nextTag();
XmlTokenStream tokens = new XmlTokenStream(sr, XML);
XmlTokenStream tokens = new XmlTokenStream(sr, XML,
FromXmlParser.Feature.collectDefaults());
assertEquals(XmlTokenStream.XML_START_ELEMENT, tokens.getCurrentToken());
assertEquals("root", tokens.getLocalName());
assertEquals(XmlTokenStream.XML_ATTRIBUTE_NAME, tokens.next());
Expand All @@ -54,7 +57,8 @@ public void testEmptyTags() throws Exception
XMLStreamReader sr = XMLInputFactory.newInstance().createXMLStreamReader(new StringReader(XML));
// must point to START_ELEMENT, so:
sr.nextTag();
XmlTokenStream tokens = new XmlTokenStream(sr, XML);
XmlTokenStream tokens = new XmlTokenStream(sr, XML,
FromXmlParser.Feature.collectDefaults());
assertEquals(XmlTokenStream.XML_START_ELEMENT, tokens.getCurrentToken());
assertEquals("root", tokens.getLocalName());
assertEquals(XmlTokenStream.XML_START_ELEMENT, tokens.next());
Expand All @@ -69,7 +73,8 @@ public void testNested() throws Exception
String XML = "<root><a><b><c>abc</c></b></a></root>";
XMLStreamReader sr = XMLInputFactory.newInstance().createXMLStreamReader(new StringReader(XML));
sr.nextTag();
XmlTokenStream tokens = new XmlTokenStream(sr, XML);
XmlTokenStream tokens = new XmlTokenStream(sr, XML,
FromXmlParser.Feature.collectDefaults());
assertEquals(XmlTokenStream.XML_START_ELEMENT, tokens.getCurrentToken());
assertEquals("root", tokens.getLocalName());
assertEquals(XmlTokenStream.XML_START_ELEMENT, tokens.next());
Expand Down

0 comments on commit 95e4b9a

Please sign in to comment.