This discussion is archived
10 Replies Latest reply: Aug 19, 2010 8:38 AM by 843807 RSS

XML-Encoder ignores classes that have a Enum as property. Why?

843807 Newbie
Currently Being Moderated
Hi,

the example below produces this output:
<?xml version="1.0" encoding="UTF-8"?> 
<java version="1.6.0_21" class="java.beans.XMLDecoder"> 
 <object class="test.Test" method="valueOf"> 
  <string>A</string> 
 </object> 
 <object class="test.Test" method="valueOf"> 
  <string>B</string> 
 </object> 
</java> 
Cause I had put TestEnumAsProperty to the stream this is wrong and If I read the stream I will get only two enum instances of Test. Why isn't TestEnumAsProperty written properly?

The PersistenceDelegate which is commented out, didn't solved it neither.

If you switch between the two statements in the for-loop, you will get the same result, which indicates that the first result must be wrong, cause you can't tell any more how this result was produced and so you can't read it properly any more.

Thanks.

Greetings Michael
import java.beans.*;
import java.io.*;



public class TestEnumsWith_XMLEncoder {

    /* Target filename */
    private static final String targetFilename = ".\\TestPdfs\\enc_out.xml";

    public static void main(final String args[]) {
     final File target = new File(targetFilename);
     openXMLEncoder(target);
     openXMLDecoder(target);
     System.exit(0);
    }

    private static void openXMLDecoder(final File file) {
     FileInputStream fis = null;
     BufferedInputStream in = null;
     XMLDecoder dec = null;

     try {
         if (file.exists() && file.canRead()) {
          fis = new FileInputStream(file);
          in = new BufferedInputStream(fis);
          dec = new XMLDecoder(in);
          dec.setExceptionListener(new ExceptionListener() {
              public void exceptionThrown(final Exception e) {
               e.printStackTrace();
              }
          });
          System.out.println(dec.readObject().getClass().getName());

          // final TestEnumAsProperty testEnum = (TestEnumAsProperty)
          // dec.readObject();

         }
     }
     catch (final Throwable e) {
         e.printStackTrace();
     }
     finally {
         if (dec != null) {
          dec.close();
         }
         if (in != null) {
          try {
              in.close();
          }
          catch (final IOException ioe) {
              ioe.printStackTrace();
          }
         }
         if (fis != null) {
          try {
              fis.close();
          }
          catch (final IOException ioe) {
              ioe.printStackTrace();
          }
         }
     }
    }

    private static void openXMLEncoder(final File file) {
     FileOutputStream fos = null;
     BufferedOutputStream out = null;
     XMLEncoder enc = null;

     try {
         if (file != null) {
          System.out.println(file.getAbsolutePath());
          if (file.exists()) {
              file.delete();
          }
          file.createNewFile();
          if (file.canWrite()) {

              fos = new FileOutputStream(file);
              out = new BufferedOutputStream(fos);
              enc = new XMLEncoder(new BufferedOutputStream(fos));

              enc.setExceptionListener(new ExceptionListener() {
               /*                                                                                                             * Avoid multiple popups of error warnings */
               public void exceptionThrown(final Exception e) {
                   e.printStackTrace();
               }
              });

              // enc.setPersistenceDelegate(TestEnumAsProperty.class, new
              // DefaultPersistenceDelegate(
              // new String[] { "t" }));

              for (final Test obj : Test.values()) {
               enc.writeObject(new TestEnumAsProperty(obj));
               // enc.writeObject(obj);
              }

              enc.close();
          }

         }
     }
     catch (final Throwable e) {
         e.printStackTrace();
     }
     finally {
         if (enc != null) {
          enc.close();
         }
         if (out != null) {
          try {
              out.close();
          }
          catch (final IOException ioe) {
              ioe.printStackTrace();
          }
         }
         if (fos != null) {
          try {
              fos.close();
          }
          catch (final IOException ioe) {
              ioe.printStackTrace();
          }
         }

     }
    }
}
public class TestEnumAsProperty {

    private Test t;

    public TestEnumAsProperty() {

    }

    public TestEnumAsProperty(final Test t) {
     this.t = t;
    }

    /**
     * @return the a
     */
    public Test getT() {
     return this.t;
    }

    /**
     * @param a
     *            the a to set
     */
    public void setT(final Test t) {
     this.t = t;
    }

}
public enum Test {
    A, B;
}
  • 1. Re: XML-Encoder ignores classes that have a Enum as property. Why?
    843807 Newbie
    Currently Being Moderated
    The above issue seem to be solve by upgrading to 6.21 or i may have missed up something.

    But when I write out the map as last element (see below) I'm getting this output:
    <?xml version="1.0" encoding="UTF-8"?> 
    <java version="1.6.0_21" class="java.beans.XMLDecoder"> 
     <object class="test.TestEnumAsProperty"> 
      <void id="Test0" property="t"/> 
     </object> 
     <object idref="Test0"/> 
     <object class="test.TestEnumAsProperty"> 
      <void property="t"> 
       <object id="Test1" class="test.Test" method="valueOf"> 
        <string>B</string> 
       </object> 
      </void> 
     </object> 
     <object idref="Test1"/> 
     <object class="java.util.TreeMap"> 
      <void method="put"> 
       <object idref="Test0"/> 
       <object class="test.TestEnumAsProperty"/> 
      </void> 
      <void method="put"> 
       <object idref="Test1"/> 
       <object class="test.TestEnumAsProperty"> 
        <void property="t"> 
         <object idref="Test1"/> 
        </void> 
       </object> 
      </void> 
     </object> 
    </java> 
    No A is written out.

    If I write it before everything else, I get this output:
    <?xml version="1.0" encoding="UTF-8"?> 
    <java version="1.6.0_21" class="java.beans.XMLDecoder"> 
     <object class="java.util.TreeMap"> 
      <void method="put"> 
       <object id="Test0" class="test.Test" method="valueOf"> 
        <string>A</string> 
       </object> 
       <object class="test.TestEnumAsProperty"/> 
      </void> 
      <void method="put"> 
       <object id="Test1" class="test.Test" method="valueOf"> 
        <string>B</string> 
       </object> 
       <object class="test.TestEnumAsProperty"> 
        <void property="t"> 
         <object idref="Test1"/> 
        </void> 
       </object> 
      </void> 
     </object> 
     <object class="test.TestEnumAsProperty"/> 
     <object idref="Test0"/> 
     <object class="test.TestEnumAsProperty"> 
      <void property="t"> 
       <object idref="Test1"/> 
      </void> 
     </object> 
     <object idref="Test1"/> 
    </java> 
    A is written out.

    In the demo app this has no effect, but in my current app this fact raises this error during loading.
    I couldn't reproduce that error in the demo app, yet.
    [ERROR]       [AWT-EventQueue-0] "2010-08-18 18:52:24,015"       io.XML_ReadOperations$1  
           exceptionThrown Unbound variable: AktiveBilanzObjekte2 at Line 82 
    java.lang.Exception: Unbound variable: AktiveBilanzObjekte2
         at com.sun.beans.ObjectHandler.simulateException(ObjectHandler.java:148)
         at com.sun.beans.ObjectHandler.lookup(ObjectHandler.java:344)
         at com.sun.beans.ObjectHandler.startElement(ObjectHandler.java:275)
         at com.sun.org.apache.xerces.internal.parsers.AbstractSAXParser.startElement(AbstractSAXParser.java:453)
         at com.sun.org.apache.xerces.internal.parsers.AbstractXMLDocumentParser.emptyElement(AbstractXMLDocumentParser.java:179)
         at com.sun.org.apache.xerces.internal.impl.XMLDocumentFragmentScannerImpl.scanStartElement(XMLDocumentFragmentScannerImpl.java:1343)
         at com.sun.org.apache.xerces.internal.impl.XMLDocumentFragmentScannerImpl$FragmentContentDriver.next(XMLDocumentFragmentScannerImpl.java:2755)
         at com.sun.org.apache.xerces.internal.impl.XMLDocumentScannerImpl.next(XMLDocumentScannerImpl.java:648)
         at com.sun.org.apache.xerces.internal.impl.XMLDocumentFragmentScannerImpl.scanDocument(XMLDocumentFragmentScannerImpl.java:511)
         at com.sun.org.apache.xerces.internal.parsers.XML11Configuration.parse(XML11Configuration.java:808)
         at com.sun.org.apache.xerces.internal.parsers.XML11Configuration.parse(XML11Configuration.java:737)
         at com.sun.org.apache.xerces.internal.parsers.XMLParser.parse(XMLParser.java:119)
         at com.sun.org.apache.xerces.internal.parsers.AbstractSAXParser.parse(AbstractSAXParser.java:1205)
         at com.sun.org.apache.xerces.internal.jaxp.SAXParserImpl$JAXPSAXParser.parse(SAXParserImpl.java:522)
         at javax.xml.parsers.SAXParser.parse(SAXParser.java:364)
         at javax.xml.parsers.SAXParser.parse(SAXParser.java:142)
         at java.beans.XMLDecoder.getHandler(XMLDecoder.java:238)
         at java.beans.XMLDecoder.readObject(XMLDecoder.java:201)
         ....
    AktiveBilanzObjekte2 is an Enum which is used as pproperty in one class and as key-values in many Treemaps (cause EnumMaps don't work with XMLEncoder before Java7).

    I can't control the sequence, cause I'm writing out one single Object graph.
    I can't get rid of the default value, cause it would causes a lot of Nullpointerexceptions.

    I think A isn't written out cause XML-Encoder threats this as unchanged property in the first case, instead of an Enum.

    Thanks.

    Greetings Michael

    Edited by: Urmech on Aug 18, 2010 10:39 AM
  • 2. Re: XML-Encoder ignores classes that have a Enum as property. Why?
    843807 Newbie
    Currently Being Moderated
    import java.beans.*;
    import java.io.*;
    import java.util.Map;
    import java.util.TreeMap;
    
    
    public class TestEnumsWith_XMLEncoder {
    
        /* Target filename */
        private static final String targetFilename = ".\\TestPdfs\\enc_out.xml";
    
        public static void main(final String args[]) {
         final File target = new File(targetFilename);
         openXMLEncoder(target);
         openXMLDecoder(target);
         System.exit(0);
        }
    
        private static void openXMLDecoder(final File file) {
         FileInputStream fis = null;
         BufferedInputStream in = null;
         XMLDecoder dec = null;
    
         try {
             if (file.exists() && file.canRead()) {
              fis = new FileInputStream(file);
              in = new BufferedInputStream(fis);
              dec = new XMLDecoder(in);
              dec.setExceptionListener(new ExceptionListener() {
                  public void exceptionThrown(final Exception e) {
                   e.printStackTrace();
                  }
              });
    
              final TestEnumAsProperty testEnum = (TestEnumAsProperty) dec.readObject();
              final Test a = (Test) dec.readObject();
              final TestEnumAsProperty testEnum2 = (TestEnumAsProperty) dec.readObject();
              final Test b = (Test) dec.readObject();
              final Map<Test, TestEnumAsProperty> map = (Map<Test, TestEnumAsProperty>) dec.readObject();
    
             }
         }
         catch (final Throwable e) {
             e.printStackTrace();
         }
         finally {
             if (dec != null) {
              dec.close();
             }
             if (in != null) {
              try {
                  in.close();
              }
              catch (final IOException ioe) {
                  ioe.printStackTrace();
              }
             }
             if (fis != null) {
              try {
                  fis.close();
              }
              catch (final IOException ioe) {
                  ioe.printStackTrace();
              }
             }
         }
        }
    
        private static void openXMLEncoder(final File file) {
         FileOutputStream fos = null;
         BufferedOutputStream out = null;
         XMLEncoder enc = null;
    
         try {
             if (file != null) {
              System.out.println(file.getAbsolutePath());
              if (file.exists()) {
                  file.delete();
              }
              file.createNewFile();
              if (file.canWrite()) {
    
                  fos = new FileOutputStream(file);
                  out = new BufferedOutputStream(fos);
                  enc = new XMLEncoder(new BufferedOutputStream(fos));
    
                  enc.setExceptionListener(new ExceptionListener() {
                   /*                                                                                                                                      * Avoid multiple popups of error warnings */
                   public void exceptionThrown(final Exception e) {
                       e.printStackTrace();
                   }
                  });
    
                  final Map<Test, TestEnumAsProperty> map = new TreeMap<Test, TestEnumAsProperty>();
                  for (final Test obj : Test.values()) {
                   map.put(obj, new TestEnumAsProperty(obj));
                  }
    
                  enc.writeObject(new TestEnumAsProperty());
                  enc.writeObject(Test.A);
                  enc.writeObject(new TestEnumAsProperty(Test.B));
                  enc.writeObject(Test.B);
                  enc.writeObject(map);
                  enc.close();
              }
    
             }
         }
         catch (final Throwable e) {
             e.printStackTrace();
         }
         finally {
             if (enc != null) {
              enc.close();
             }
             if (out != null) {
              try {
                  out.close();
              }
              catch (final IOException ioe) {
                  ioe.printStackTrace();
              }
             }
             if (fos != null) {
              try {
                  fos.close();
              }
              catch (final IOException ioe) {
                  ioe.printStackTrace();
              }
             }
    
         }
        }
    }
    public class TestEnumAsProperty {
    
        private Test t;
    
        public TestEnumAsProperty() {
         this.t = Test.A;
        }
    
        public TestEnumAsProperty(final Test t) {
         this.t = t;
        }
    
        /**
         * @return the t
         */
        public Test getT() {
         return this.t;
        }
    
        /**
         * @param a
         *            the t to set
         */
        public void setT(final Test t) {
         this.t = t;
        }
    
    }
  • 3. Re: XML-Encoder ignores classes that have a Enum as property. Why?
    EJP Guru
    Currently Being Moderated
    This is off topic, but:
         if (file.exists() && file.canRead()) {
    You don't need that. new FileInputStream() will throw a FileNotFoundException if the file isn't there, and you have to catch IOException anyway, so why write extra code? You don't even have an 'else' for this so as it is the method will fail silently if the file isn't there. Removing this code will improve the method.
              if (file.exists()) {
              file.delete();
              }
              file.createNewFile();
              if (file.canWrite()) {
    Similarly you don't need any of that. new FileOutputStream() will delete the file if it exists, create a new one, and throw an IOException if it can't do any of that. Once again you don't have an 'else' for any of this so the method will fail silently, and once again removing this code will improve the method.

    It is possible that these silent failures are contributing to this problem.
  • 4. Re: XML-Encoder ignores classes that have a Enum as property. Why?
    843807 Newbie
    Currently Being Moderated
    Hi, thanks for your reply.

    I just copied that function from my project and removed the unnecessary stuff, such as user-interaction if the file already exists and so on. Also normally an exception is more expensive than the call of a method, especially if you can avoid claiming resources. If they are silent that is a problem true, but they are only silent after removing the other stuff.

    Back to my topic, I tried writing the enum out before everything else, but XMLEncoder sets no ref to that item, so it still fails with an "java.lang.Exception: Unbound variable: AktiveBilanzObjekte2" exception.

    So it seems that to removed the default value, is the only possible solution. Or can I tell XMLEncoder e. g. by PropertyDescriptor that it should write it out always?

    Thanks.

    Greetings Michael

    PS: I solved it by removing the the default value and taking care of all the NullPointer-Exceptions, but be aware that setting it to the default in the get-method didn't cured it in my case.

    Edited by: Urmech on Aug 19, 2010 1:40 AM
  • 5. Re: XML-Encoder ignores classes that have a Enum as property. Why?
    EJP Guru
    Currently Being Moderated
    Also normally an exception is more expensive than the call of a method
    That argument is completely fallacious.

    (a) You are forcing two or three disk accesses where one would do. Compared to that the alleged overhead is entirely insignificant, especially as it is only thrown in exceptional cases.

    (b) There is another and more serious problem: the timing window your code opens up. What if the input file exists when you call File.exists() and is deleted before you get to open it? You still have the possibility of the exception, you still have to deal with it, you still have the alleged overhead, and all you have accomplished is coding two error paths instead of one, and doubling or tripling the number of system calls and disk overhead.

    The best way to detect the availability of any resource is to try to use it.
    especially if you can avoid claiming resources.
    I don't know what that means. Exceptions don't claim resources and neither does code that catches them when thrown by new XXX() constructors.
  • 6. Re: XML-Encoder ignores classes that have a Enum as property. Why?
    843807 Newbie
    Currently Being Moderated
    Hi, the risk that another process modifies is true, but a very rare condition and a failure acceptable.
    If you don't use if/file.exists()){ askuser} else {writefile}; you have to check this during exception handling, where you would have to start the next try-catch block, which leads to code duplication.

    By claiming resources, I mean:
     fos = new FileOutputStream(tempFile);
                  out = new BufferedOutputStream(fos);
                  enc = new XMLEncoder(new BufferedOutputStream(fos));
    "PS: I solved it by removing the the default value and taking care of all the NullPointer-Exceptions, but be aware that setting it to the default in the get-method didn't cured it in my case."

    During additional tests I ran into serious other problems, caused by removing the default value. So I'm still interested in another solution.

    Maybe related with this
    bug .


    Greetings Michael
  • 7. Re: XML-Encoder ignores classes that have a Enum as property. Why?
    EJP Guru
    Currently Being Moderated
    Completely wrong. You have to catch and handle the exceptions anyway, so forcing yourself to write all the same stuff again in else blocks is where the duplication comes in. And File.create() and/or File.delete() followed by new FileOutputStream realiy is inefficient, as the same action will happen anyway in new FileOutputStream(). And removing all that junk code also solves the timing-window problem.
  • 8. Re: XML-Encoder ignores classes that have a Enum as property. Why?
    843807 Newbie
    Currently Being Moderated
    Hi,
    I still think that code like this
    if (file.exists()) {
                  final int answer = JOptionPane.showConfirmDialog(MainFrame.INSTANCE.getFrame(),
                       "File exists already! Do you wish do overwrite it?", "Overwrite File?",
                       JOptionPane.YES_NO_OPTION);
                  if (answer == JOptionPane.NO_OPTION) {
                   cleanUpAfterWork(false);
                   return;
                  }
              }
    // rest of method 
    is much better to read, than catching the exception, finding out the cause, ask the user and then to start all over again.
    If you ask the user there will always be a logical second, while another process can interfere.
    If I switch the reading code to a method which throws an IOException I can avoid code duplication in the exception branch, that is true.


    By the way my other serious problems, where caused by enc.flush(), which had written out some objects twice, on which I rely on that the fact they are having the same reference. So I fixed it now by removing the default value and avoided flush.

    Thanks for your efforts.

    Greetings Michael
  • 9. Re: XML-Encoder ignores classes that have a Enum as property. Why?
    EJP Guru
    Currently Being Moderated
    That is a completely new discussion. At least that code has a plausible motivation. The code I commented on doesn't. It is actually less efficient in the normal case.
  • 10. Re: XML-Encoder ignores classes that have a Enum as property. Why?
    843807 Newbie
    Currently Being Moderated
    Hi,

    as I mentioned I just copied the function and removed the unnecessary stuff, so I replaced the user interaction just with file.delete(), which is already done by FileOutputStream if you don't append. Which is unnecessary in the example as you mentioned.

    Also
    if (tempFile.canWrite()) {
    has no obvious additional value and should probably be removed. At least it should have an else.clause to prompt that fact the user.

    I'm not sure, when the exception will be raised, so I taught that this would be cheaper, than doing all the stuff until I first try to write to the stream. The exception may be raised only after calling flush or the close-method.

    FileOutputStream throws only a FileNotFoundException (SecurityException is no issue here), so it's most likely that an not writeable file, would be detected only after trying to write to it, which can be very late. So the purpose of this instruction was for me, to figure out that problem as soon as possible.

    So I think I should add an else-clause here.

    Greetings Michael