Fix: null as a JavaBean property was not handled properly
diff --git a/README b/README
index 1264bc7..f41e9b6 100644
--- a/README
+++ b/README
@@ -105,6 +105,7 @@
== History ==
'''(in Mecrurial)'''
+ * Fix: null as a `JavaBean` property was not handled properly (thanks to Magne)
* Fix [http://trac-hg.assembla.com/snakeyaml/ticket/40 ticket #40]: java.sql.Timestamp was not handled properly (thanks to Magne)
'''1.2 (2009-04-27)'''
diff --git a/doc/wiki/Documentation.txt b/doc/wiki/Documentation.txt
index 8034fa6..c7d2149 100644
--- a/doc/wiki/Documentation.txt
+++ b/doc/wiki/Documentation.txt
@@ -673,7 +673,8 @@
[http://trac-hg.assembla.com/snakeyaml/browser/src/test/java/org/yaml/snakeyaml/constructor/ImplicitTagsTest.java here]
If it is nessesary to massage/skip the root global tag for the `JavaBean` it
-can be specified via `DumperOptions`.setExplicitRoot(String tag).
+can be specified via `DumperOptions`.setExplicitRoot(String tag). ([http://yaml.org/type/map.html tag:yaml.org,2002:map] can be used to eliminate
+the class name of the `JavaBean`)
There is a utility to parse `JavaBeans` - [http://trac-hg.assembla.com/snakeyaml/browser/src/main/java/org/yaml/snakeyaml/JavaBeanParser.java JavaBeanParser].
All the methods of this utility are stateless and can be called from different Threads. The utility eliminates
diff --git a/src/changes/changes.xml b/src/changes/changes.xml
index 9443bda..9f19419 100644
--- a/src/changes/changes.xml
+++ b/src/changes/changes.xml
@@ -6,7 +6,10 @@
<author email="py4fun@gmail.com">Andrey Somov</author>
</properties>
<body>
- <release version="1.2-SNAPSHOT" date="in Mercurial" description="development">
+ <release version="1.2-SNAPSHOT" date="in Mercurial" description="development">
+ <action dev="py4fun" type="fix" due-to="Magne">
+ Fix: null as a JavaBean property was not handled properly (2009-06-04)
+ </action>
<action dev="py4fun" type="update">
Validate changes.xml file (2009-05-25)
</action>
@@ -14,7 +17,7 @@
Fix: getting an error when javabean contains java.sql.Timestamp fields (2009-05-25)
</action>
</release>
- <release version="1.2" date="2009-04-27" description="expose low-level API">
+ <release version="1.2" date="2009-04-27" description="expose low-level API">
<action dev="py4fun" type="add">
Add 'Yaml.parse()' method which return Events to support low level YAML processing (2009-04-20)
</action>
@@ -109,11 +112,11 @@
Refactor: use Enum instead of String as NodeId (2009-02-19)
</action>
</release>
- <release version="1.0.1" date="2009-02-18" description="implement Enum support">
- <action dev="py4fun" type="fix">
+ <release version="1.0.1" date="2009-02-18" description="implement Enum support">
+ <action dev="py4fun" type="fix">
Do not emit anchors for Enum (2009-02-18)
</action>
- <action dev="py4fun" type="fix">
+ <action dev="py4fun" type="fix">
Enum as a JavaBean property (when the Enum class is implicitly defined) does
not need tags for both loading and dumping (2009-02-17)
</action>
@@ -225,7 +228,7 @@
</action>
</release>
<release version="0.9" date="2008-01-12" description="Add possibility to define a root class for Loader">
- <action dev="py4fun" type="add">
+ <action dev="py4fun" type="add">
Finish 2.27 example from the specification (2009-01-12)
</action>
<action dev="py4fun" type="add">
@@ -274,7 +277,7 @@
</action>
</release>
<release version="0.7" date="2008-12-20" description="Improve test coverage">
- <action dev="py4fun" type="update">
+ <action dev="py4fun" type="update">
Improve test coverage for constructor package (2008-12-20)
</action>
<action dev="py4fun" type="remove">
@@ -316,7 +319,7 @@
defaultFlowStyle for Dumper is configurable in DumperOptions (2008-12-12)
</action>
</release>
- <release version="0.5" date="2008-12-12" description="Import PyYAML 3.06">
+ <release version="0.5" date="2008-12-12" description="Import PyYAML 3.06">
<action dev="py4fun" type="add">
Add possibility to define an implicit resolver. {359:63190d5bcd10} (2008-12-11)
</action>
@@ -395,7 +398,7 @@
Rewrite Parser from scratch. (2008-11-17)
</action>
</release>
- <release version="0.4" date="2008-11-11" description="Fix issues in Scanner">
+ <release version="0.4" date="2008-11-11" description="Fix issues in Scanner">
<action dev="py4fun" type="update">
Move constants from Yaml interface to appropriate classes (2008-11-10)
</action>
@@ -466,7 +469,7 @@
Respect sign for float.
Fix issue: https://jvyaml.dev.java.net/issues/show_bug.cgi?id=13 (2008-10-28)
</action>
- <action dev="py4fun" type="add">
+ <action dev="py4fun" type="add">
Binary data is represented as ByteBuffer (2008-10-27)
</action>
<action dev="py4fun" type="fix">
@@ -489,12 +492,12 @@
<action dev="py4fun" type="update">
Mavenize project. Apply standard Maven folder structure (2008-10-20)
</action>
- </release>
- <release version="0.2.1" date="2008-10-20" description="Import JvYaml from CVS">
- <action dev="py4fun" type="add">
- Import project from https://jvyaml.dev.java.net/ (2008-10-20)
- </action>
- </release>
- </body>
+ </release>
+ <release version="0.2.1" date="2008-10-20" description="Import JvYaml from CVS">
+ <action dev="py4fun" type="add">
+ Import project from https://jvyaml.dev.java.net/ (2008-10-20)
+ </action>
+ </release>
+ </body>
</document>
diff --git a/src/main/java/org/yaml/snakeyaml/DumperOptions.java b/src/main/java/org/yaml/snakeyaml/DumperOptions.java
index f254e1f..d6bd038 100644
--- a/src/main/java/org/yaml/snakeyaml/DumperOptions.java
+++ b/src/main/java/org/yaml/snakeyaml/DumperOptions.java
@@ -212,8 +212,8 @@
/**
* @param expRoot
- * - tag to be used for the root node or <code>null</code> to get
- * the default
+ * - tag to be used for the root node. (JavaBeans may use
+ * "tag:yaml.org,2002:map")
*/
public void setExplicitRoot(String expRoot) {
if (expRoot == null) {
diff --git a/src/main/java/org/yaml/snakeyaml/constructor/Constructor.java b/src/main/java/org/yaml/snakeyaml/constructor/Constructor.java
index f0f8d31..3483564 100644
--- a/src/main/java/org/yaml/snakeyaml/constructor/Constructor.java
+++ b/src/main/java/org/yaml/snakeyaml/constructor/Constructor.java
@@ -152,7 +152,7 @@
@Override
protected Object callConstructor(Node node) {
- if (Object.class.equals(node.getType())) {
+ if (Object.class.equals(node.getType()) || "tag:yaml.org,2002:null".equals(node.getTag())) {
return super.callConstructor(node);
}
Object result;
diff --git a/src/test/java/org/yaml/snakeyaml/JavaBeanWithNullValues.java b/src/test/java/org/yaml/snakeyaml/JavaBeanWithNullValues.java
new file mode 100644
index 0000000..1ecd17f
--- /dev/null
+++ b/src/test/java/org/yaml/snakeyaml/JavaBeanWithNullValues.java
@@ -0,0 +1,95 @@
+/*
+ * See LICENSE file in distribution for copyright and licensing information.
+ */
+package org.yaml.snakeyaml;
+
+import java.sql.Timestamp;
+import java.util.Date;
+
+public class JavaBeanWithNullValues {
+ private String string;
+ private Integer integer;
+ private Float float1;
+ private Double double1;
+ private Long long1;
+ private Date date;
+ private java.sql.Date sqlDate;
+ private Timestamp timestamp;
+ private Boolean boolean1;
+
+ public JavaBeanWithNullValues() {
+ }
+
+ public String getString() {
+ return string;
+ }
+
+ public void setString(String string) {
+ this.string = string;
+ }
+
+ public Integer getInteger() {
+ return integer;
+ }
+
+ public void setInteger(Integer integer) {
+ this.integer = integer;
+ }
+
+ public Float getFloat1() {
+ return float1;
+ }
+
+ public void setFloat1(Float float1) {
+ this.float1 = float1;
+ }
+
+ public Double getDouble1() {
+ return double1;
+ }
+
+ public void setDouble1(Double double1) {
+ this.double1 = double1;
+ }
+
+ public Long getLong1() {
+ return long1;
+ }
+
+ public void setLong1(Long long1) {
+ this.long1 = long1;
+ }
+
+ public Date getDate() {
+ return date;
+ }
+
+ public void setDate(Date date) {
+ this.date = date;
+ }
+
+ public java.sql.Date getSqlDate() {
+ return sqlDate;
+ }
+
+ public void setSqlDate(java.sql.Date sqlDate) {
+ this.sqlDate = sqlDate;
+ }
+
+ public Timestamp getTimestamp() {
+ return timestamp;
+ }
+
+ public void setTimestamp(Timestamp timestamp) {
+ this.timestamp = timestamp;
+ }
+
+ public Boolean getBoolean1() {
+ return boolean1;
+ }
+
+ public void setBoolean1(Boolean boolean1) {
+ this.boolean1 = boolean1;
+ }
+
+}
diff --git a/src/test/java/org/yaml/snakeyaml/JavaBeanWithNullValuesTest.java b/src/test/java/org/yaml/snakeyaml/JavaBeanWithNullValuesTest.java
new file mode 100644
index 0000000..589adf2
--- /dev/null
+++ b/src/test/java/org/yaml/snakeyaml/JavaBeanWithNullValuesTest.java
@@ -0,0 +1,129 @@
+/*
+ * See LICENSE file in distribution for copyright and licensing information.
+ */
+package org.yaml.snakeyaml;
+
+import java.sql.Timestamp;
+import java.util.Date;
+
+import junit.framework.TestCase;
+
+public class JavaBeanWithNullValuesTest extends TestCase {
+
+ public void testNotNull() throws Exception {
+ String dumpStr = dumpJavaBeanWithNullValues(false);
+ // System.out.println(dumpStr);
+ Yaml yaml = new Yaml();
+ JavaBeanWithNullValues parsed = (JavaBeanWithNullValues) yaml.load(dumpStr);
+ assertNotNull(parsed.getString());
+ assertNotNull(parsed.getBoolean1());
+ assertNotNull(parsed.getDate());
+ assertNotNull(parsed.getDouble1());
+ assertNotNull(parsed.getFloat1());
+ assertNotNull(parsed.getInteger());
+ assertNotNull(parsed.getLong1());
+ assertNotNull(parsed.getSqlDate());
+ assertNotNull(parsed.getTimestamp());
+ //
+ parsed = JavaBeanParser.load(dumpStr, JavaBeanWithNullValues.class);
+ assertNotNull(parsed.getString());
+ assertNotNull(parsed.getBoolean1());
+ assertNotNull(parsed.getDate());
+ assertNotNull(parsed.getDouble1());
+ assertNotNull(parsed.getFloat1());
+ assertNotNull(parsed.getInteger());
+ assertNotNull(parsed.getLong1());
+ assertNotNull(parsed.getSqlDate());
+ assertNotNull(parsed.getTimestamp());
+ }
+
+ public void testNull() throws Exception {
+ String dumpStr = dumpJavaBeanWithNullValues(true);
+ // System.out.println(dumpStr);
+ Yaml yaml = new Yaml();
+ JavaBeanWithNullValues parsed = (JavaBeanWithNullValues) yaml.load(dumpStr);
+ assertNull(parsed.getString());
+ //
+ parsed = JavaBeanParser.load(dumpStr, JavaBeanWithNullValues.class);
+ assertNull(parsed.getString());
+ }
+
+ public void testNullStringAndBoolean() throws Exception {
+ JavaBeanWithNullValues javaBeanWithNullValues = new JavaBeanWithNullValues();
+ DumperOptions options = new DumperOptions();
+ options.setDefaultScalarStyle(DumperOptions.ScalarStyle.DOUBLE_QUOTED);
+ options.setExplicitStart(true);
+ options.setExplicitEnd(true);
+ Yaml yaml = new Yaml(options);
+ javaBeanWithNullValues.setBoolean1(null);
+ javaBeanWithNullValues.setDate(new Date(System.currentTimeMillis()));
+ javaBeanWithNullValues.setDouble1(1d);
+ javaBeanWithNullValues.setFloat1(1f);
+ javaBeanWithNullValues.setInteger(1);
+ javaBeanWithNullValues.setLong1(1l);
+ javaBeanWithNullValues.setSqlDate(new java.sql.Date(System.currentTimeMillis()));
+ javaBeanWithNullValues.setString(null); // ok
+ javaBeanWithNullValues.setTimestamp(new Timestamp(System.currentTimeMillis()));
+
+ String dumpStr = yaml.dump(javaBeanWithNullValues);
+ // System.out.println(dumpStr);
+ yaml = new Yaml();
+ JavaBeanWithNullValues parsed = (JavaBeanWithNullValues) yaml.load(dumpStr);
+ assertNull(" expect null, got " + parsed.getBoolean1(), parsed.getBoolean1());
+ assertNull(" expect null, got " + parsed.getString(), parsed.getString());
+ }
+
+ public void testNoRootTag() throws Exception {
+ JavaBeanWithNullValues javaBeanWithNullValues = new JavaBeanWithNullValues();
+ DumperOptions options = new DumperOptions();
+ options.setDefaultScalarStyle(DumperOptions.ScalarStyle.DOUBLE_QUOTED);
+ options.setExplicitStart(true);
+ options.setExplicitEnd(true);
+ options.setExplicitRoot("tag:yaml.org,2002:map");
+ Yaml yaml = new Yaml(options);
+ javaBeanWithNullValues.setBoolean1(null);
+ javaBeanWithNullValues.setDate(new Date(System.currentTimeMillis()));
+ javaBeanWithNullValues.setDouble1(1d);
+ javaBeanWithNullValues.setFloat1(1f);
+ javaBeanWithNullValues.setInteger(1);
+ javaBeanWithNullValues.setLong1(1l);
+ javaBeanWithNullValues.setSqlDate(new java.sql.Date(System.currentTimeMillis()));
+ javaBeanWithNullValues.setString(null); // ok
+ javaBeanWithNullValues.setTimestamp(new Timestamp(System.currentTimeMillis()));
+
+ String dumpStr = yaml.dump(javaBeanWithNullValues);
+ assertFalse("No explicit root tag must be used.", dumpStr
+ .contains("JavaBeanWithNullValues"));
+ // System.out.println(dumpStr);
+ yaml = new Yaml();
+ JavaBeanWithNullValues parsed = JavaBeanParser.load(dumpStr, JavaBeanWithNullValues.class);
+ assertNull(" expect null, got " + parsed.getBoolean1(), parsed.getBoolean1());
+ assertNull(" expect null, got " + parsed.getString(), parsed.getString());
+ assertEquals(1d, parsed.getDouble1());
+ assertEquals(1f, parsed.getFloat1());
+ assertEquals(new Integer(1), parsed.getInteger());
+ assertEquals(new Long(1l), parsed.getLong1());
+ }
+
+ private String dumpJavaBeanWithNullValues(boolean nullValues) {
+ JavaBeanWithNullValues javaBeanWithNullValues = new JavaBeanWithNullValues();
+ DumperOptions options = new DumperOptions();
+ options.setDefaultScalarStyle(DumperOptions.ScalarStyle.DOUBLE_QUOTED);
+ options.setExplicitStart(true);
+ options.setExplicitEnd(true);
+ Yaml yaml = new Yaml(options);
+ if (nullValues) {
+ return yaml.dump(javaBeanWithNullValues);
+ }
+ javaBeanWithNullValues.setBoolean1(false);
+ javaBeanWithNullValues.setDate(new Date(System.currentTimeMillis()));
+ javaBeanWithNullValues.setDouble1(1d);
+ javaBeanWithNullValues.setFloat1(1f);
+ javaBeanWithNullValues.setInteger(1);
+ javaBeanWithNullValues.setLong1(1l);
+ javaBeanWithNullValues.setSqlDate(new java.sql.Date(System.currentTimeMillis()));
+ javaBeanWithNullValues.setString(""); // ok
+ javaBeanWithNullValues.setTimestamp(new Timestamp(System.currentTimeMillis()));
+ return yaml.dump(javaBeanWithNullValues);
+ }
+}
diff --git a/src/test/java/org/yaml/snakeyaml/constructor/BeanConstructorTest.java b/src/test/java/org/yaml/snakeyaml/constructor/BeanConstructorTest.java
index 0346829..3a2ce95 100644
--- a/src/test/java/org/yaml/snakeyaml/constructor/BeanConstructorTest.java
+++ b/src/test/java/org/yaml/snakeyaml/constructor/BeanConstructorTest.java
@@ -96,13 +96,8 @@
"Invalid node Character: 'id'; length: 2"));
}
document = "charClass: #";
- try {
- yaml.load(document);
- fail("Only one char must be allowed.");
- } catch (Exception e) {
- assertTrue(e.getMessage(), e.getMessage().contains(
- "Invalid node Character: ''; length: 0"));
- }
+ TestBean1 bean = (TestBean1) yaml.load(document);
+ assertNull("Null must be accepted.", bean.getCharClass());
document = "charClass: ''";
try {
yaml.load(document);
@@ -112,14 +107,11 @@
"Invalid node Character: ''; length: 0"));
}
document = "charClass:\n";
- try {
- yaml.load(document);
- fail("Only one char must be allowed.");
- } catch (Exception e) {
- assertTrue(e.getMessage(), e.getMessage().contains(
- "Invalid node Character: ''; length: 0"));
- }
-
+ bean = (TestBean1) yaml.load(document);
+ assertNull("Null must be accepted.", bean.getCharClass());
+ document = "charClass: 1\n";
+ bean = (TestBean1) yaml.load(document);
+ assertEquals(new Character('1'), bean.getCharClass());
}
public void testNoEmptyConstructor() throws IOException {