diff --git a/src/com/esotericsoftware/kryo/serializers/CompatibleFieldSerializer.java b/src/com/esotericsoftware/kryo/serializers/CompatibleFieldSerializer.java index b6c3c371d..4ed8185b4 100644 --- a/src/com/esotericsoftware/kryo/serializers/CompatibleFieldSerializer.java +++ b/src/com/esotericsoftware/kryo/serializers/CompatibleFieldSerializer.java @@ -31,6 +31,7 @@ import com.esotericsoftware.kryo.io.OutputChunked; import com.esotericsoftware.kryo.util.ObjectMap; import com.esotericsoftware.kryo.util.Util; +import java.util.ArrayList; /** Serializes objects using direct field assignment, providing both forward and backward compatibility. This means fields can be * added or removed without invalidating previously serialized bytes. Renaming or changing the type of a field is not supported. @@ -64,11 +65,18 @@ public void write (Kryo kryo, Output output, T object) { CachedField[] fields = cachedFields.fields; ObjectMap context = kryo.getGraphContext(); + ArrayList fieldsList = new ArrayList(); if (!context.containsKey(this)) { if (TRACE) trace("kryo", "Write fields for class: " + type.getName()); context.put(this, null); output.writeVarInt(fields.length, true); for (int i = 0, n = fields.length; i < n; i++) { + fieldsList.add(fields[i].name); + if (i + 1 < n) { + if (fieldsList.contains(fields[i+1].name)) + throw new IllegalArgumentException(fields[i].field.getDeclaringClass() + + " has the same field as the super class, config.ExtendedFieldNames must be true"); + } if (TRACE) trace("kryo", "Write field name: " + fields[i].name + pos(output.position())); output.writeString(fields[i].name); } diff --git a/test/com/esotericsoftware/kryo/serializers/CompatibleFieldSerializerTest.java b/test/com/esotericsoftware/kryo/serializers/CompatibleFieldSerializerTest.java index 829382467..8274ca065 100644 --- a/test/com/esotericsoftware/kryo/serializers/CompatibleFieldSerializerTest.java +++ b/test/com/esotericsoftware/kryo/serializers/CompatibleFieldSerializerTest.java @@ -24,17 +24,28 @@ import com.esotericsoftware.kryo.Kryo; import com.esotericsoftware.kryo.KryoException; import com.esotericsoftware.kryo.KryoTestCase; +import com.esotericsoftware.kryo.Serializer; +import com.esotericsoftware.kryo.SerializerFactory; import com.esotericsoftware.kryo.SerializerFactory.CompatibleFieldSerializerFactory; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.IOException; import java.io.Serializable; import java.util.Arrays; import java.util.List; import java.util.Objects; +import com.esotericsoftware.kryo.io.Input; +import com.esotericsoftware.kryo.io.Output; +import com.esotericsoftware.kryo.util.DefaultInstantiatorStrategy; import org.apache.commons.lang.builder.EqualsBuilder; +import org.junit.Assert; import org.junit.Rule; import org.junit.Test; import org.junit.rules.ExpectedException; +import org.objenesis.strategy.SerializingInstantiatorStrategy; /** @author Nathan Sweet */ public class CompatibleFieldSerializerTest extends KryoTestCase { @@ -642,4 +653,117 @@ public int hashCode () { return Objects.hash(value, list, serializable); } } + // #699 check ExtendedFieldName for CompatibleFieldSerializer + static public class ExtendedFieldNamesTest { + @Test(expected = IllegalArgumentException.class) + public void setExtendedFieldNamesDefault() throws IOException { + final Child child = new Child(); + child.setCustomNote(true); + child.setCid(3); + Kryo kryo = getKryo(false); + final File outputFile = File.createTempFile("temp_output", "dat"); + try (final Output output = new Output(new FileOutputStream(outputFile))) { + kryo.writeObject(output, child); + } + } + + @Test + public void setExtendedFieldNamesIsTrue() throws IOException { + final Child child = new Child(); + child.setCustomNote(true); + child.setCid(3); + Kryo kryo = getKryo(true); + final File outputFile = File.createTempFile("temp_output", "dat"); + try (final Output output = new Output(new FileOutputStream(outputFile))) { + kryo.writeObject(output, child); + final Input input = new Input(new FileInputStream(outputFile)); + final Child restoreChild = kryo.readObject(input, Child.class); + Assert.assertEquals(true, restoreChild.customNote); + input.close(); + } + } + + private Kryo getKryo(Boolean isSetExtendedFieldNames) { + final Kryo kryo = new Kryo(); + kryo.setDefaultSerializer(new SerializerFactory.BaseSerializerFactory() { + @Override + public Serializer newSerializer(Kryo kryo, Class type) { + final CompatibleFieldSerializer.CompatibleFieldSerializerConfig config = + new CompatibleFieldSerializer.CompatibleFieldSerializerConfig(); + config.setChunkedEncoding(true); + config.setReadUnknownFieldData(true); + if (isSetExtendedFieldNames) + config.setExtendedFieldNames(true); + final CompatibleFieldSerializer serializer = new CompatibleFieldSerializer(kryo, type, config); + return serializer; + } + }); + kryo.setRegistrationRequired(false); + kryo.setInstantiatorStrategy(new DefaultInstantiatorStrategy(new SerializingInstantiatorStrategy())); + return kryo; + } + + private class Child extends Father { + private Integer cid = 1; + private Boolean customNote = true; + + public void setCid(Integer cid) { + this.cid = cid; + } + + public int getCid() { + return this.cid; + } + + public void setCustomNote(Boolean customNote) { + this.customNote = customNote; + } + + public Boolean getCustomNote() { + return this.customNote; + } + } + + private class Father extends Grandpa { + private Integer fid = 1; + private String name = "Alan"; + + public void setFid(Integer fid) { + this.fid = fid; + } + + public int getFid() { + return this.fid; + } + + public void setName(String name) { + this.name = name; + } + + public String getName() { + return this.name; + } + } + + private class Grandpa implements Serializable { + private Integer gid = 1; + private Boolean customNote = false; + + public void setGid(Integer gid) { + this.gid = gid; + } + + public int getGid() { + return this.gid; + } + + public void setCustomNote(Boolean customNote) { + this.customNote = customNote; + } + + public Boolean getCustomNote() { + return this.customNote; + } + } + } }