kryo icon indicating copy to clipboard operation
kryo copied to clipboard

Kryo5 deserialization lost Locale script field

Open hanbing204 opened this issue 1 year ago • 1 comments

Describe the bug java.util.Locale script field in the is missing after deserialization with Kryo5, when we execute below UT method. we expect to get sr_RS_#Cyrl but actually we get the sr_RS which script is lost

expected [sr_RS_#Cyrl] but found [sr_RS]
java.lang.AssertionError: expected [sr_RS_#Cyrl] but found [sr_RS]

To Reproduce

  @Test
  public void testLocaleScript() {
    Kryo kryo = new Kryo();
    kryo.setRegistrationRequired(false);

    Locale locale = new Locale.Builder().setLanguage("sr").setScript("Cyrl").setRegion("RS").build();

    ByteArrayOutputStream baos = new ByteArrayOutputStream();
    try (Output output = new Output(baos)) {
      kryo.writeClassAndObject(output, locale);
    }
    
    Object deserializedLocale;
    try (Input input = new Input(baos.toByteArray())){
      deserializedLocale = kryo.readClassAndObject(input);
    }

    assertEquals(deserializedLocale.toString(), "sr_RS_#Cyrl");
  }


Environment:

  • OS: Windows/OSX/Ubuntu
  • JDK Version: JDK17
  • Kryo Version: 5.5.0

hanbing204 avatar Mar 05 '24 03:03 hanbing204

It is quite trivial to add support for script to LocaleSerializer, but I'm afraid it is not possible to do it in a backwards-compatible way.

For now, you can register your own script-aware serializer for locales:

public static class ScriptAwareLocaleSerializer extends ImmutableSerializer<Locale> {

    public void write (Kryo kryo, Output output, Locale l) {
        output.writeAscii(l.getLanguage());
        output.writeAscii(l.getCountry());
        output.writeString(l.getVariant());
        output.writeAscii(l.getScript());
    }

    public Locale read (Kryo kryo, Input input, Class<? extends Locale> type) {
        String language = input.readString();
        String country = input.readString();
        String variant = input.readString();
        String script = input.readString();
        return create(language, country, varian, script);
    }

    protected Locale create (String language, String country, String variant) {
        if (script != null) {
            return new Locale.Builder()
                    .setLanguage(language)
                    .setRegion(country)
                    .setVariant(variant)
                    .setScript(script)
                    .build();
        } else {
            return new Locale(language, country, variant);
        }
    }
}
kryo.addDefaultSerializer(Locale.class, ScriptAwareLocaleSerializer.class);

It might make sense to make this the default in Kryo 6.

theigl avatar Mar 06 '24 10:03 theigl