什么可以在序列化和存储在Jar文件中时修改SerialVersionUID?

时间:2009-07-07 13:09:15

标签: java serialization jar drools

我在序列化对象时面临一些问题(我正在使用JBoss Drools,并希望存储KnowledgePackage的ArrayList)。

当我序列化列表时,将结果存储在一个文件中,并反序列化它,没有问题,所以它工作正常。

但是当我序列化列表时,将结果存储在字节流中,然后将其保存在JarFile中,然后我无法反序列化结果,因为这个错误:

IOException during package import : java.util.ArrayList; local class incompatible: stream classdesc serialVersionUID = 8664875232659988799, local class serialVersionUID = 8683452581122892189

所以我认为问题是我将序列化对象保存到Jarfile条目中。我认为我这样做是正确的,因为可以正确读取在Jarfile中以相同方式保存的其他文件。 在使用'cmp'和'hexdump'之后,我发现保存它,如果uuid,一个jar会导致一个八位字节的变化,否则内容是相同的。

我真的很失望,无法说出问题所在。

什么可以修改两个类之间的SerialVersionUID?除了另一个vm版本?


添加源代码:  exportToJar - > writeRulesPackageEntry - > writeEntry

/**
 * Writes content provided from a reader into a file contained in a jar.
 * 
 * @param output the output stream to write on
 * @param entryName the name of the file that will contain reader data
 * @param contentReader 
 * 
 * @return the zip entry that has been created into the jar
 */
ZipEntry writeEntry(JarOutputStream output, String entryName, ByteArrayInputStream input) {
    if (output == null || entryName == null || entryName.trim().length() == 0 || input == null) {
        throw new NullPointerException("Null argument passed");
    }

    ZipEntry entry = new ZipEntry(entryName);
    byte[] buffer = new byte[BUFFER_LENGTH];

    try {
        output.putNextEntry(entry);
        int nRead;

        while ((nRead = input.read(buffer, 0, BUFFER_LENGTH)) > 0) {
            output.write(buffer, 0, nRead);
        }

        output.closeEntry();
    } catch (IOException e) {
        e.printStackTrace();
    }

    return entry;
}

/**
 * Export rules files to a serialized object (ArrayList<KnowledgePackage>) into 
 * an output stream, then write the output content as an entry of a jar.
 * 
 * @param os the output jar to write in
 */
void writeRulesPackageEntry(JarOutputStream os) {
    // serialize objects and write them to the output stream
    ByteArrayOutputStream output = new ByteArrayOutputStream();
    RulesPackaging rulesPackaging = new RulesPackaging();
    rulesPackaging.exportResources(this.rules, output);

    // create a new input stream to read written objects from
    ByteArrayInputStream input = new ByteArrayInputStream(output.toByteArray());
    this.writeEntry(os, Product.ENTRY_RULES_PACKAGE, input);
}

/**
 * Creates a JarFile containing resources. 
 * 
 * @param filename the exported jar filename
 * @return the jar as an object, null if an error occured
 */
public JarFile exportToJar(String filename) {
    FileOutputStream fOs;
    JarOutputStream jOs;
    JarFile jar = null;

    try {
        fOs = new FileOutputStream(filename);
        jOs = new JarOutputStream(fOs);

        this.writeRulesPackageEntry(jOs);

        jOs.close();

        // construct a jar from the output jar
        jar = new JarFile(new File(filename));
    } catch (IOException e) {
        e.printStackTrace();
    }

    return jar;
}

3 个答案:

答案 0 :(得分:3)

serialVersionUID不会改变。除非在源代码中明确指定了值,否则它是在编译时分配的static final(基于源代码的哈希值)。

这里有更多关于http://mindprod.com/jgloss/serialization.html的信息。

除非您看到java.util.ArrayList的正确serialVersionUID是8683452581122892189L,它在源代码中明确指定,并且自1.2中引入该类以来保持不变。

正如您所说,当发送到JarFile的字节流时,很可能发生错误 - 请发布您正在使用的代码。

在发布源代码后续了

我怀疑问题在于使用java.io.InputStreamReader

来自JavaDoc:

  

InputStreamReader是来自的桥梁   字节流到字符流:它   读取字节并将其解码为   使用指定字符集的字符。   它使用的charset可能是   由名称指定或可能给出   显式,或平台的默认值   charset可能被接受。

一旦我看到非文本流中涉及的字符集,我总是会产生怀疑,因为在解码过程中可能会修改流是一个字节序列与字符集中的字符不对应(见过编码问题发生时出现的那些小方块字符)。我会尝试使用java.io.ByteArrayInputStream中的java.io.InputStreamReader直接读取writeRulesPackageEntry(JarOutputStream)上的字节。无需转换为char[]

答案 1 :(得分:0)

就像尼克提出的那样,问题很可能是你没有将流视为字节(永远不会改变),而是作为字符(可以是)。

话虽如此,关于序列化的另一个合适的资源是我在一百万年前(1997年)写的一本专门的章节,“掌握JavaBeans”。幸运的是,第11章“序列化”与今天的情况一样重要。从http://ccd.uab.es/~srobles/manuals/JavaBeans

下载免费的PDF

答案 2 :(得分:0)

是否有可能将早期版本序列化到JarFile中,并且后续序列化尝试无法覆盖它?然后你将检索该类的早期版本的序列化数据,这将(正确地)抛出“不兼容”错误。

我问的原因是,当我更新了一个序列化类但尚未删除旧的持久缓存时,我使用我选择的缓存系统(EHCache)看到了类似的错误消息。