Search code examples
javajackson-dataformat-xml

How to use Jackson dataformat.xml to serialize a LookAndFeel object in java?


In Java JDK 14.0.1,

When I try to serialize:

private LookAndFeel lookAndFeel = new FlatLightLaf();
// I am obviously creating this in a working object as the serializing in xml with jackson has been successful until this specific object was added.

It is using https://www.formdev.com/flatlaf/ being stored as a maven dependency.

Using com.fasterxml.jackson.dataformat.xml that I have in my pom.xml along with com.fasterxml.woodstox-core as I have heard that can prevent certain errors:

    <dependency>
      <groupId>com.formdev</groupId>
      <artifactId>flatlaf</artifactId>
      <version>0.36</version>
    </dependency>
    <dependency>
      <groupId>com.fasterxml.jackson.dataformat</groupId>
      <artifactId>jackson-dataformat-xml</artifactId>
      <version>2.11.1</version>
    </dependency>
    <dependency>
       <groupId>com.fasterxml.woodstox</groupId>
       <artifactId>woodstox-core</artifactId>
       <version>5.1.0</version>
    </dependency>

with the following serializing function:

    public void serializeSettings(Settings settings, File directoryToStore) {
        XmlMapper xmlMapper = new XmlMapper();
        try {
            xmlMapper.writeValue(directoryToStore, settings);
            
        } catch (JsonGenerationException e) {
            e.printStackTrace();
            
        } catch (JsonMappingException e) {
            e.printStackTrace();
            
        } catch (IOException e) {
            e.printStackTrace();
            
        }
    }

which is using a settings object (The one I will be providing is not the full object and only a small as possible representation of what variable is causing the issue, keep in mind I have deleted or shortened most of the java doc to make it a little shorter to read):

Settings.java

package com.pygame_studio.settings;

import java.io.File;

import javax.swing.LookAndFeel;

import com.pygame_studio.settings.appearance_and_behavior.AppearanceAndBehavior;

public class Settings {
    private File storedSettingsFile;  // Where the settings .xml file is stored.
    
    private AppearanceAndBehavior appearanceAndBehavior;  // Stores all the settings to do with the appearance and behavior of Pygame Studio.

    public Settings() {
        super();
    }
    
    public Settings(File storedSettingsFile,
                    /*Appearance And Behavior settings*/
                    LookAndFeel lookAndFeel) {
        this.setStoredSettingsFile(storedSettingsFile);
        
        this.setAppearanceAndBehavior(new AppearanceAndBehavior(lookAndFeel));
    }
    
    public Settings(File storedSettingsFile,
                    AppearanceAndBehavior appearanceAndBehavior) {
        this.setStoredSettingsFile(storedSettingsFile);
        
        this.setAppearanceAndBehavior(appearanceAndBehavior);
    }
    
    /**
     * @return storedSettingsFile
     */
    public File getStoredSettingsFile() {
        return storedSettingsFile;
    }

    /**
     * @param storedSettingsFile - storedSettingsFile to set.
     */
    public void setStoredSettingsFile(File storedSettingsFile) {
        this.storedSettingsFile = storedSettingsFile;
    }

    /**
     * @return appearanceAndBehavior
     */
    public AppearanceAndBehavior getAppearanceAndBehavior() {
        return appearanceAndBehavior;
    }

    /**
     * @param appearanceAndBehavior - appearanceAndBehavior to set.
     */
    public void setAppearanceAndBehavior(AppearanceAndBehavior appearanceAndBehavior) {
        this.appearanceAndBehavior = appearanceAndBehavior;
    }

}

AppearanceAndAppearance.java

package com.pygame_studio.settings.appearance_and_behavior;

import java.io.File;

import javax.swing.LookAndFeel;

public class AppearanceAndBehavior {
    private LookAndFeel lookAndFeel;  // Stores the look and feel of the program.

    public AppearanceAndBehavior() {
        super();
    }
    
    public AppearanceAndBehavior(LookAndFeel lookAndFeel) {
        this.setLookAndFeel(lookAndFeel);
    }
    
    /**
     * @return - lookAndFeel
     */
    public LookAndFeel getLookAndFeel() {
        return this.lookAndFeel;
    }

    /**
     * @param lookAndFeel - lookAndFeel to set
     */
    public void setLookAndFeel(LookAndFeel lookAndFeel) {
        this.lookAndFeel = lookAndFeel;
    }

}

I get the following error:

com.fasterxml.jackson.databind.exc.InvalidDefinitionException: No serializer found for class com.formdev.flatlaf.UIDefaultsLoader$$Lambda$93/0x0000000800c2f040 and no properties discovered to create BeanSerializer (to avoid exception, disable SerializationFeature.FAIL_ON_EMPTY_BEANS) (through reference chain: com.pygame_studio.settings.Settings["appearanceAndBehavior"]->com.pygame_studio.settings.appearance_and_behavior.AppearanceAndBehavior["lookAndFeel"]->com.formdev.flatlaf.FlatLightLaf["defaults"]->javax.swing.UIDefaults["CheckBoxMenuItem.border"])
    at com.fasterxml.jackson.databind.exc.InvalidDefinitionException.from(InvalidDefinitionException.java:77)
    at com.fasterxml.jackson.databind.SerializerProvider.reportBadDefinition(SerializerProvider.java:1277)
    at com.fasterxml.jackson.databind.DatabindContext.reportBadDefinition(DatabindContext.java:400)
    at com.fasterxml.jackson.databind.ser.impl.UnknownSerializer.failForEmpty(UnknownSerializer.java:71)
    at com.fasterxml.jackson.databind.ser.impl.UnknownSerializer.serialize(UnknownSerializer.java:33)
    at com.fasterxml.jackson.databind.ser.std.MapSerializer.serializeFields(MapSerializer.java:726)
    at com.fasterxml.jackson.databind.ser.std.MapSerializer.serializeWithoutTypeInfo(MapSerializer.java:681)
    at com.fasterxml.jackson.databind.ser.std.MapSerializer.serialize(MapSerializer.java:637)
    at com.fasterxml.jackson.databind.ser.std.MapSerializer.serialize(MapSerializer.java:33)
    at com.fasterxml.jackson.databind.ser.BeanPropertyWriter.serializeAsField(BeanPropertyWriter.java:728)
    at com.fasterxml.jackson.dataformat.xml.ser.XmlBeanSerializerBase.serializeFields(XmlBeanSerializerBase.java:212)
    at com.fasterxml.jackson.dataformat.xml.ser.XmlBeanSerializer.serialize(XmlBeanSerializer.java:129)
    at com.fasterxml.jackson.databind.ser.BeanPropertyWriter.serializeAsField(BeanPropertyWriter.java:728)
    at com.fasterxml.jackson.dataformat.xml.ser.XmlBeanSerializerBase.serializeFields(XmlBeanSerializerBase.java:212)
    at com.fasterxml.jackson.dataformat.xml.ser.XmlBeanSerializer.serialize(XmlBeanSerializer.java:129)
    at com.fasterxml.jackson.databind.ser.BeanPropertyWriter.serializeAsField(BeanPropertyWriter.java:728)
    at com.fasterxml.jackson.dataformat.xml.ser.XmlBeanSerializerBase.serializeFields(XmlBeanSerializerBase.java:212)
    at com.fasterxml.jackson.dataformat.xml.ser.XmlBeanSerializer.serialize(XmlBeanSerializer.java:129)
    at com.fasterxml.jackson.dataformat.xml.ser.XmlSerializerProvider.serializeValue(XmlSerializerProvider.java:109)
    at com.fasterxml.jackson.databind.ObjectMapper._configAndWriteValue(ObjectMapper.java:4374)
    at com.fasterxml.jackson.databind.ObjectMapper.writeValue(ObjectMapper.java:3570)
    at com.pygame_studio.settings.SettingsManager.serializeSettings(SettingsManager.java:41)
    at com.pygame_studio.PygameStudio.<init>(PygameStudio.java:24)
    at com.pygame_studio.PygameStudio.main(PygameStudio.java:31)

I understand that "CheckBoxMenuItem.border" probably does not have a public getter and setter due to research I have done on this error, but I do not think I can change this.

I have tried a solution from: Serializing with Jackson (JSON) - getting "No serializer found"?

putting: xmlMapper.setVisibility(javax.swing.UIDefaults.CheckBoxMenuItem.border.FIELD, Visibility.ANY); Straight after initialising xmlMapper.

However this produces an error saying that CheckBoxMenuItem attribute does not exist.

Why is the No serializer found error occurring?

How do I fix it?

EDIT: As suggested I tried xmlMapper.configure(SerializationFeature.FAIL_ON_EMPTY_BEANS, false); inside of my serializeSettings() method however I got the following warnings:

WARNING: An illegal reflective access operation has occurred
WARNING: Illegal reflective access by com.fasterxml.jackson.databind.util.ClassUtil (file:/C:/Users/Eno/.m2/repository/com/fasterxml/jackson/core/jackson-databind/2.11.1/jackson-databind-2.11.1.jar) to method sun.awt.SunHints$Value.getIndex()
WARNING: Please consider reporting this to the maintainers of com.fasterxml.jackson.databind.util.ClassUtil
WARNING: Use --illegal-access=warn to enable warnings of further illegal reflective access operations
WARNING: All illegal access operations will be denied in a future release

Also, when I tried to deserialize them I got a plethora of more errors, so I do not think that this is working.


Solution

  • Do not try to serialize class FlatLightLaf (or any other LookAndFeel class). It is not designed to be serializable and it makes no sense to do so. I'm the author of FlatLaf.

    Instead use the class name (as String) of the look and feel.

    For searialization use:

    String className = UIManager.getLookAndFeel().getClass().getName();
    // write look and feel class name
    

    For deserialization use:

    String className = ... // read look and feel class name
    UIManager.setLookAndFeel( className );