简体   繁体   中英

Jackson Cannot Find a (Map) Key Deserializer for Path?

I have a strange issue where I have a HashMap that I serialize for later use on the disk.

It is a HashMap<Path, ConverterMetadata> the ConverterMetadata being a custom class that I wrote to keep track of music file metadata.

The ConverterMetadata appears to have proper tags and in my testing I have confirmed that Jackson can write and read Map<Path, String> instances, so I'm not entirely sure what is happening here, and why it says that it is breaking on the key (Path) object.

Here is the exception, the class, the JSON outputted, and the method that reads/writes it:

The Exception:

Exception in thread "main" com.fasterxml.jackson.databind.exc.InvalidDefinitionException: Cannot find a (Map) Key deserializer for type [simple type, class java.nio.file.Path]
 at [Source: (File); line: 1, column: 1]
    at com.fasterxml.jackson.databind.exc.InvalidDefinitionException.from(InvalidDefinitionException.java:67)
    at com.fasterxml.jackson.databind.DeserializationContext.reportBadDefinition(DeserializationContext.java:1452)
    at com.fasterxml.jackson.databind.deser.DeserializerCache._handleUnknownKeyDeserializer(DeserializerCache.java:599)
    at com.fasterxml.jackson.databind.deser.DeserializerCache.findKeyDeserializer(DeserializerCache.java:168)
    at com.fasterxml.jackson.databind.DeserializationContext.findKeyDeserializer(DeserializationContext.java:500)
    at com.fasterxml.jackson.databind.deser.std.MapDeserializer.createContextual(MapDeserializer.java:248)
    at com.fasterxml.jackson.databind.DeserializationContext.handleSecondaryContextualization(DeserializationContext.java:682)
    at com.fasterxml.jackson.databind.DeserializationContext.findRootValueDeserializer(DeserializationContext.java:482)
    at com.fasterxml.jackson.databind.ObjectMapper._findRootDeserializer(ObjectMapper.java:4191)
    at com.fasterxml.jackson.databind.ObjectMapper._readMapAndClose(ObjectMapper.java:4010)
    at com.fasterxml.jackson.databind.ObjectMapper.readValue(ObjectMapper.java:2922)
    at com.protonmail.sarahszabo.stellar.conversions.SpaceBridge.initBridge(SpaceBridge.java:151)
    at com.protonmail.sarahszabo.stellar.StellarMode$2.start(StellarMode.java:87)
    at com.protonmail.sarahszabo.stellar.Main.stellarConversion(Main.java:203)
    at com.protonmail.sarahszabo.stellar.Main.main(Main.java:77)

ConverterMetadata Class:

/**
 * A representation of .opus metadata. Used in concordance with a
 * {@link StellarOPUSConverter}. All fields are immutable.
 */
public final class ConverterMetadata {

    /**
     * The default metadata instance.
     */
    public static final ConverterMetadata DEFAULT_METADATA = new ConverterMetadata("Unknown Artist",
            "Unknown Title", Main.FULL_PROGRAM_NAME, LocalDate.MAX, StellarGravitonField.newPath(""), Integer.MAX_VALUE);

    @JsonProperty
    private final String artist;
    @JsonProperty
    private final String title;
    @JsonProperty
    private final String createdBy;
    @JsonProperty
    private final LocalDate stellarIndexDate;
    @JsonProperty
    private final Path albumArtPath;
    @JsonProperty
    private final int bitrate;

    /**
     * Constructs a new {@link ConverterMetadata} with the specified arguments.
     *
     *
     * @param artist The artist for this track
     * @param title The title of this track
     * @param createdBy The program that created this track/last modified this
     * track
     * @param date The date this track was created
     * @param albumArtPath The path to the album art
     * @param bitrate The bitrate of the track
     */
    @JsonCreator
    public ConverterMetadata(@JsonProperty(value = "artist") String artist,
            @JsonProperty(value = "title") String title, @JsonProperty(value = "createdBy") String createdBy,
            @JsonProperty(value = "stellarIndexDate") LocalDate date, @JsonProperty(value = "albumArtPath") Path albumArtPath,
            @JsonProperty(value = "bitrate") int bitrate) {
        //Do Consructor Stuff Here
    }
}

Code that Writes/Reads from the Ledger File AKA initBridge():

Map<Path, ConverterMetadata> LIBRARY_LEDGER = new HashMap<>();
//Earlier in the code, write ledger, to disk
MAPPER.writeValue(LIBRARY_LEDGER_PATH.toFile(), LIBRARY_LEDGER);
//Later we read the ledger    
Map<Path, ConverterMetadata> previousLedger = MAPPER.readValue(LIBRARY_LEDGER_PATH.toFile(),
                        new TypeReference<HashMap<Path, ConverterMetadata>>() {
                });
                LIBRARY_LEDGER.putAll(previousLedger);

JSON in the File:

{"/home/sarah/Music/Indexing/Playlists/Best Playlist/Spiral.opus":{"artist":"Vangelis","title":"Spiral","createdBy":"Stellar OPUS Conversion Library 1.4α","stellarIndexDate":[2018,7,23],"albumArtPath":"file:///tmp/Stellar%20OPUS%20Converter%20Temporary%20Directory15723231348656772389/ReIndexing/Spiral.png","bitrate":320},"/home/sarah/Music/Indexing/Playlists/Finished/Aphelion.opus":{"artist":"Scandroid","title":"Aphelion","createdBy":"Stellar OPUS Conversion Library 1.4α","stellarIndexDate":[2018,8,8],"albumArtPath":"file:///tmp/Stellar%20OPUS%20Converter%20Temporary%20Directory15723231348656772389/ReIndexing/Aphelion.png","bitrate":320}

POM:

    <properties>
  ...
  <!-- Use the latest version whenever possible. -->
  <jackson.version>2.9.8</jackson.version>
  ...
</properties>

<dependencies>
  ...
  <dependency>
    <groupId>com.fasterxml.jackson.core</groupId>
    <artifactId>jackson-databind</artifactId>
    <version>${jackson.version}</version>
  </dependency>
  ...
</dependencies>

You need to implement key deserialiser for java.nio.file.Path class. It could like below:

class PathKeyDeserializer extends KeyDeserializer {

    @Override
    public Object deserializeKey(String key, DeserializationContext ctxt) {
        return Paths.get(key);
    }
}

You can register it and use like in below example:

import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.DeserializationContext;
import com.fasterxml.jackson.databind.KeyDeserializer;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature;
import com.fasterxml.jackson.databind.module.SimpleModule;

import java.io.File;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.HashMap;
import java.util.Map;

public class JsonApp {

    public static void main(String[] args) throws Exception {
        Map<Path, String> path2String = new HashMap<>();
        path2String.put(Paths.get("user", "downloads"), "Downloads");
        path2String.put(Paths.get("home", "des"), "Desktop");

        SimpleModule nioModule = new SimpleModule();
        nioModule.addKeyDeserializer(Path.class, new PathKeyDeserializer());

        ObjectMapper mapper = new ObjectMapper();
        mapper.enable(SerializationFeature.INDENT_OUTPUT);
        mapper.registerModule(nioModule);

        String json = mapper.writeValueAsString(path2String);
        System.out.println(json);

        path2String = mapper.readValue(json, new TypeReference<HashMap<Path, String>>() {});
        System.out.println(path2String);
    }

}

Above code prints:

{
  "home/des" : "Desktop",
  "user/downloads" : "Downloads"
}
{home/des=Desktop, user/downloads=Downloads}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM