I got an issue when trying to deserialise xml using jackson.
I am using the following function to deserialise my xml from the file.
The Xml file is as-:
https://github.com/Eno-Gerguri/Pygame-Studio/blob/master/Settings/defaultSettings.xml
Here is the function I'm using to deserialise the object:
public Settings deserializeSettings(File settingsFile) {
XmlMapper xmlMapper = new XmlMapper();
String xml = null;
Settings settings = null;
try {
xml = inputStreamToString(new FileInputStream(settingsFile));
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
try {
settings = xmlMapper.readValue(xml, Settings.class);
} catch (JsonMappingException e) {
e.printStackTrace();
} catch (JsonProcessingException e) {
e.printStackTrace();
}
return settings;
}
private String inputStreamToString(InputStream inputStream) throws IOException {
StringBuilder stringBuilder = new StringBuilder();
String line;
BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(inputStream));
while ((line = bufferedReader.readLine()) != null) {
stringBuilder.append(line);
}
bufferedReader.close();
return stringBuilder.toString();
}
The Settings object I'm using that I am deserialising into:-
The sub-class that the Settings object uses:
The sub-sub-class that the Settings object uses:
When I try to deserialise the object in a separate file:
private Settings defaultSettings = settingsManager.deserializeSettings(DefaultSettings.DEFAULT_SETTINGS_FILE_DIRECTORY);
The following error log
com.fasterxml.jackson.databind.JsonMappingException: N/A
at [Source: (StringReader); line: 1, column: 356] (through reference chain: com.pygame_studio.settings.Settings["appearanceAndBehavior"]->com.pygame_studio.settings.appearance_and_behavior.AppearanceAndBehavior["font"]->com.pygame_studio.settings.appearance_and_behavior.Font["fontDirectory"])
at com.fasterxml.jackson.databind.JsonMappingException.from(JsonMappingException.java:278)
at com.fasterxml.jackson.databind.deser.SettableBeanProperty._throwAsIOE(SettableBeanProperty.java:611)
at com.fasterxml.jackson.databind.deser.SettableBeanProperty._throwAsIOE(SettableBeanProperty.java:599)
at com.fasterxml.jackson.databind.deser.impl.MethodProperty.deserializeAndSet(MethodProperty.java:143)
at com.fasterxml.jackson.databind.deser.BeanDeserializer.vanillaDeserialize(BeanDeserializer.java:288)
at com.fasterxml.jackson.databind.deser.BeanDeserializer.deserialize(BeanDeserializer.java:151)
at com.fasterxml.jackson.databind.deser.impl.MethodProperty.deserializeAndSet(MethodProperty.java:129)
at com.fasterxml.jackson.databind.deser.BeanDeserializer.vanillaDeserialize(BeanDeserializer.java:288)
at com.fasterxml.jackson.databind.deser.BeanDeserializer.deserialize(BeanDeserializer.java:151)
at com.fasterxml.jackson.databind.deser.impl.MethodProperty.deserializeAndSet(MethodProperty.java:129)
at com.fasterxml.jackson.databind.deser.BeanDeserializer.vanillaDeserialize(BeanDeserializer.java:288)
at com.fasterxml.jackson.databind.deser.BeanDeserializer.deserialize(BeanDeserializer.java:151)
at com.fasterxml.jackson.databind.ObjectMapper._readMapAndClose(ObjectMapper.java:4202)
at com.fasterxml.jackson.databind.ObjectMapper.readValue(ObjectMapper.java:3205)
at com.fasterxml.jackson.databind.ObjectMapper.readValue(ObjectMapper.java:3173)
at com.pygame_studio.settings.SettingsManager.deserializeSettings(SettingsManager.java:75)
at com.pygame_studio.start_menu.StartMenu.<init>(StartMenu.java:24)
at com.pygame_studio.PygameStudio.<init>(PygameStudio.java:18)
at com.pygame_studio.PygameStudio.main(PygameStudio.java:25)
Caused by: java.lang.NullPointerException
at com.pygame_studio.settings.appearance_and_behavior.Font.setFontDirectory(Font.java:115)
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.base/java.lang.reflect.Method.invoke(Method.java:564)
at com.fasterxml.jackson.databind.deser.impl.MethodProperty.deserializeAndSet(MethodProperty.java:141)
... 15 more
I know that in the function: com.pygame_studio.settings.appearance_and_behavior.Font.setFontDirectory
public void setFontDirectory(String fontDirectory) {
if (externalFonts.containsKey(fontDirectory)) {
this.fontDirectory = externalFonts.get(fontDirectory);
} else if (localFonts.contains(fontDirectory)) {
this.fontDirectory = fontDirectory;
} else {
this.fontDirectory = this.getFallbackFont();
}
}
externalFonts Hashtable is null because it has not been initialised however, it should be as in the constructer:
public Font() {
super();
}
public Font(File externalFontDirectory, String fontName, int fontStyle, int fontSize, String fallbackFont) {
this.setExternalFontDirectory(externalFontDirectory);
this.setExternalFonts(); // External fonts is set before the font directory.
this.setLocalFonts();
this.setFontDirectory(fontName); // Font directory being set after externalFonts.
this.setFontStyle(fontStyle);
this.setFontSize(fontSize);
this.setFallbackFont(fallbackFont);
}
setExternalFonts method:
public void setExternalFonts() {
externalFonts = getExternalFonts(this.externalFontDirectory);
}
getExternalFonts method:
public static Hashtable<String, String> getExternalFonts(File externalFontDirectory) {
Hashtable<String, String> externalFonts = new Hashtable<>();
final File[] directoryFiles = externalFontDirectory.listFiles();
if (directoryFiles != null) {
for (File file : directoryFiles) {
if (file.isDirectory()) { // If the file is a sub-directory.
externalFonts.putAll(getExternalFonts(file)); // Calls itself onto the directory.
} else if (file.getName().contains(".ttf")) { // If the file is a font.
String fontName = file.getName().replace(".ttf", ""); // Gets the name of the font.
externalFonts.put(fontName, file.getPath()); // Puts the, name of the font : font's directory, into the Hashtable.
}
}
}
return externalFonts;
}
However, my IDE Eclipse tells me that this code is never reached, when it should be to initialise externalFonts.
Why is this method not being called? How do I fix this issue so I can successfully deserialise the xml into a "Settings" object?
Thanks in advance!
The specific problem is here in the Font
class:
public static Hashtable<String, String> externalFonts;
First I would recommend changing this from Hashtable
to HashMap
- these days there is no reason (in my experience) to use Hashtable
over any of the more modern collection classes. You can read community opinions here.
Second, the field needs to be initialized, so it is not null - like you do for the localFonts
list.
So that gives us this:
public static Map<String, String> externalFonts = new HashMap();
You will then need to replace the remaining references to Hashtable
, of course.
When I made that change, it fixed the specific problem you reported.