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 {