diff --git a/api/src/main/java/lol/pyr/znpcsplus/api/NpcApi.java b/api/src/main/java/lol/pyr/znpcsplus/api/NpcApi.java
index b32c050..2faf2c8 100644
--- a/api/src/main/java/lol/pyr/znpcsplus/api/NpcApi.java
+++ b/api/src/main/java/lol/pyr/znpcsplus/api/NpcApi.java
@@ -5,6 +5,7 @@ import lol.pyr.znpcsplus.api.interaction.ActionFactory;
 import lol.pyr.znpcsplus.api.interaction.ActionRegistry;
 import lol.pyr.znpcsplus.api.npc.NpcRegistry;
 import lol.pyr.znpcsplus.api.npc.NpcTypeRegistry;
+import lol.pyr.znpcsplus.api.serialization.NpcSerializerRegistry;
 import lol.pyr.znpcsplus.api.skin.SkinDescriptorFactory;
 
 /**
@@ -46,4 +47,10 @@ public interface NpcApi {
      * @return the skin descriptor factory
      */
     SkinDescriptorFactory getSkinDescriptorFactory();
+
+    /**
+     * Gets the npc serializer registry.
+     * @return the npc serializer registry
+     */
+    NpcSerializerRegistry getNpcSerializerRegistry();
 }
diff --git a/api/src/main/java/lol/pyr/znpcsplus/api/npc/NpcRegistry.java b/api/src/main/java/lol/pyr/znpcsplus/api/npc/NpcRegistry.java
index 4722e0b..b822354 100644
--- a/api/src/main/java/lol/pyr/znpcsplus/api/npc/NpcRegistry.java
+++ b/api/src/main/java/lol/pyr/znpcsplus/api/npc/NpcRegistry.java
@@ -64,4 +64,11 @@ public interface NpcRegistry {
      * @param id The ID of the NPC entry
      */
     void delete(String id);
+
+    /**
+     * Register an NPC to this registry
+     * NpcEntry instances can be obtained through the NpcSerializer classes
+     * @param entry The npc to be registered
+     */
+    void register(NpcEntry entry);
 }
diff --git a/api/src/main/java/lol/pyr/znpcsplus/api/serialization/NpcSerializer.java b/api/src/main/java/lol/pyr/znpcsplus/api/serialization/NpcSerializer.java
new file mode 100644
index 0000000..f35cbed
--- /dev/null
+++ b/api/src/main/java/lol/pyr/znpcsplus/api/serialization/NpcSerializer.java
@@ -0,0 +1,20 @@
+package lol.pyr.znpcsplus.api.serialization;
+
+import lol.pyr.znpcsplus.api.npc.NpcEntry;
+
+public interface NpcSerializer<T> {
+    /**
+     * Serialize an npc into the type of this serializer
+     * @param entry The npc entry
+     * @return The serialized class
+     */
+    T serialize(NpcEntry entry);
+
+    /**
+     * Deserialize an npc from a serialized class
+     * Note: This npc will not be registered, you need to also register it using the NpcRegistry#register(NpcEntry) method
+     * @param model The serialized class
+     * @return The deserialized NpcEntry
+     */
+    NpcEntry deserialize(T model);
+}
diff --git a/api/src/main/java/lol/pyr/znpcsplus/api/serialization/NpcSerializerRegistry.java b/api/src/main/java/lol/pyr/znpcsplus/api/serialization/NpcSerializerRegistry.java
new file mode 100644
index 0000000..74e8d89
--- /dev/null
+++ b/api/src/main/java/lol/pyr/znpcsplus/api/serialization/NpcSerializerRegistry.java
@@ -0,0 +1,19 @@
+package lol.pyr.znpcsplus.api.serialization;
+
+public interface NpcSerializerRegistry {
+    /**
+     * Get an NpcSerializer that serializes npcs into the provided class
+     * @param clazz The class to serialize into
+     * @return The npc serializer instance
+     * @param <T> The type of the class that the serializer serializes into
+     */
+    <T> NpcSerializer<T> getSerializer(Class<T> clazz);
+
+    /**
+     * Register an NpcSerializer to be used by other plugins
+     * @param clazz The class that the serializer serializes into
+     * @param serializer The serializer itself
+     * @param <T> The type of the class that the serializer serializes into
+     */
+    <T> void registerSerializer(Class<T> clazz, NpcSerializer<T> serializer);
+}
diff --git a/plugin/src/main/java/lol/pyr/znpcsplus/ZNpcsPlus.java b/plugin/src/main/java/lol/pyr/znpcsplus/ZNpcsPlus.java
index 07aadf3..2a0d64f 100644
--- a/plugin/src/main/java/lol/pyr/znpcsplus/ZNpcsPlus.java
+++ b/plugin/src/main/java/lol/pyr/znpcsplus/ZNpcsPlus.java
@@ -38,6 +38,7 @@ import lol.pyr.znpcsplus.parsers.*;
 import lol.pyr.znpcsplus.scheduling.FoliaScheduler;
 import lol.pyr.znpcsplus.scheduling.SpigotScheduler;
 import lol.pyr.znpcsplus.scheduling.TaskScheduler;
+import lol.pyr.znpcsplus.serialization.NpcSerializerRegistryImpl;
 import lol.pyr.znpcsplus.skin.cache.MojangSkinCache;
 import lol.pyr.znpcsplus.skin.cache.SkinCacheCleanTask;
 import lol.pyr.znpcsplus.storage.NpcStorageType;
@@ -135,8 +136,9 @@ public class ZNpcsPlus {
         ActionRegistryImpl actionRegistry = new ActionRegistryImpl();
         ActionFactoryImpl actionFactory = new ActionFactoryImpl(scheduler, adventure, textSerializer, bungeeConnector);
         NpcTypeRegistryImpl typeRegistry = new NpcTypeRegistryImpl();
+        NpcSerializerRegistryImpl serializerRegistry = new NpcSerializerRegistryImpl(packetFactory, configManager, actionRegistry, typeRegistry, propertyRegistry, textSerializer);
         NpcRegistryImpl npcRegistry = new NpcRegistryImpl(configManager, this, packetFactory, actionRegistry,
-                scheduler, typeRegistry, propertyRegistry, textSerializer);
+                scheduler, typeRegistry, propertyRegistry, serializerRegistry, textSerializer);
         shutdownTasks.add(npcRegistry::unload);
 
         UserManager userManager = new UserManager();
@@ -159,7 +161,7 @@ public class ZNpcsPlus {
         pluginManager.registerEvents(new UserListener(userManager), bootstrap);
 
         registerCommands(npcRegistry, skinCache, adventure, actionRegistry,
-                typeRegistry, propertyRegistry, importerRegistry, configManager, packetFactory);
+                typeRegistry, propertyRegistry, importerRegistry, configManager, packetFactory, serializerRegistry);
 
         log(ChatColor.WHITE + " * Starting tasks...");
         if (configManager.getConfig().checkForUpdates()) {
@@ -193,7 +195,7 @@ public class ZNpcsPlus {
             }
         }
 
-        NpcApiProvider.register(bootstrap, new ZNpcsPlusApi(npcRegistry, typeRegistry, propertyRegistry, actionRegistry, actionFactory, skinCache));
+        NpcApiProvider.register(bootstrap, new ZNpcsPlusApi(npcRegistry, typeRegistry, propertyRegistry, actionRegistry, actionFactory, skinCache, serializerRegistry));
         log(ChatColor.WHITE + " * Loading complete! (" + (System.currentTimeMillis() - before) + "ms)");
         log("");
 
@@ -246,7 +248,7 @@ public class ZNpcsPlus {
     private void registerCommands(NpcRegistryImpl npcRegistry, MojangSkinCache skinCache, BukkitAudiences adventure,
                                   ActionRegistryImpl actionRegistry, NpcTypeRegistryImpl typeRegistry,
                                   EntityPropertyRegistryImpl propertyRegistry, DataImporterRegistry importerRegistry,
-                                  ConfigManager configManager, PacketFactory packetFactory) {
+                                  ConfigManager configManager, PacketFactory packetFactory, NpcSerializerRegistryImpl serializerRegistry) {
 
         Message<CommandContext> incorrectUsageMessage = context -> context.send(Component.text("Incorrect usage: /" + context.getUsage(), NamedTextColor.RED));
         CommandManager manager = new CommandManager(bootstrap, adventure, incorrectUsageMessage);
@@ -322,7 +324,7 @@ public class ZNpcsPlus {
                         .addSubcommand("save", new SaveAllCommand(npcRegistry))
                         .addSubcommand("reload", new LoadAllCommand(npcRegistry))
                         .addSubcommand("import", new ImportCommand(npcRegistry, importerRegistry))
-                        .addSubcommand("migrate", new MigrateCommand(configManager, this, packetFactory, actionRegistry, typeRegistry, propertyRegistry, textSerializer, npcRegistry.getStorage(), configManager.getConfig().storageType(), npcRegistry)))
+                        .addSubcommand("migrate", new MigrateCommand(configManager, this, packetFactory, actionRegistry, typeRegistry, propertyRegistry, textSerializer, npcRegistry.getStorage(), configManager.getConfig().storageType(), npcRegistry, serializerRegistry)))
                 .addSubcommand("holo", new MultiCommand(bootstrap.loadHelpMessage("holo"))
                         .addSubcommand("add", new HoloAddCommand(npcRegistry))
                         .addSubcommand("additem", new HoloAddItemCommand(npcRegistry))
diff --git a/plugin/src/main/java/lol/pyr/znpcsplus/ZNpcsPlusApi.java b/plugin/src/main/java/lol/pyr/znpcsplus/ZNpcsPlusApi.java
index ef9c8ee..7d272da 100644
--- a/plugin/src/main/java/lol/pyr/znpcsplus/ZNpcsPlusApi.java
+++ b/plugin/src/main/java/lol/pyr/znpcsplus/ZNpcsPlusApi.java
@@ -12,6 +12,7 @@ import lol.pyr.znpcsplus.interaction.ActionFactoryImpl;
 import lol.pyr.znpcsplus.interaction.ActionRegistryImpl;
 import lol.pyr.znpcsplus.npc.NpcRegistryImpl;
 import lol.pyr.znpcsplus.npc.NpcTypeRegistryImpl;
+import lol.pyr.znpcsplus.serialization.NpcSerializerRegistryImpl;
 import lol.pyr.znpcsplus.skin.SkinDescriptorFactoryImpl;
 import lol.pyr.znpcsplus.skin.cache.MojangSkinCache;
 
@@ -22,14 +23,16 @@ public class ZNpcsPlusApi implements NpcApi {
     private final ActionRegistryImpl actionRegistry;
     private final ActionFactoryImpl actionFactory;
     private final SkinDescriptorFactoryImpl skinDescriptorFactory;
+    private final NpcSerializerRegistryImpl npcSerializerRegistry;
 
-    public ZNpcsPlusApi(NpcRegistryImpl npcRegistry, NpcTypeRegistryImpl typeRegistry, EntityPropertyRegistryImpl propertyRegistry, ActionRegistryImpl actionRegistry, ActionFactoryImpl actionFactory, MojangSkinCache skinCache) {
+    public ZNpcsPlusApi(NpcRegistryImpl npcRegistry, NpcTypeRegistryImpl typeRegistry, EntityPropertyRegistryImpl propertyRegistry, ActionRegistryImpl actionRegistry, ActionFactoryImpl actionFactory, MojangSkinCache skinCache, NpcSerializerRegistryImpl npcSerializerRegistry) {
         this.npcRegistry = npcRegistry;
         this.typeRegistry = typeRegistry;
         this.propertyRegistry = propertyRegistry;
         this.actionRegistry = actionRegistry;
         this.actionFactory = actionFactory;
         this.skinDescriptorFactory = new SkinDescriptorFactoryImpl(skinCache);
+        this.npcSerializerRegistry = npcSerializerRegistry;
     }
 
     @Override
@@ -62,4 +65,9 @@ public class ZNpcsPlusApi implements NpcApi {
     public SkinDescriptorFactory getSkinDescriptorFactory() {
         return skinDescriptorFactory;
     }
+
+    @Override
+    public NpcSerializerRegistryImpl getNpcSerializerRegistry() {
+        return npcSerializerRegistry;
+    }
 }
diff --git a/plugin/src/main/java/lol/pyr/znpcsplus/commands/storage/MigrateCommand.java b/plugin/src/main/java/lol/pyr/znpcsplus/commands/storage/MigrateCommand.java
index cda3b3c..063b98a 100644
--- a/plugin/src/main/java/lol/pyr/znpcsplus/commands/storage/MigrateCommand.java
+++ b/plugin/src/main/java/lol/pyr/znpcsplus/commands/storage/MigrateCommand.java
@@ -11,6 +11,7 @@ import lol.pyr.znpcsplus.npc.NpcEntryImpl;
 import lol.pyr.znpcsplus.npc.NpcRegistryImpl;
 import lol.pyr.znpcsplus.npc.NpcTypeRegistryImpl;
 import lol.pyr.znpcsplus.packets.PacketFactory;
+import lol.pyr.znpcsplus.serialization.NpcSerializerRegistryImpl;
 import lol.pyr.znpcsplus.storage.NpcStorage;
 import lol.pyr.znpcsplus.storage.NpcStorageType;
 import net.kyori.adventure.text.Component;
@@ -35,8 +36,9 @@ public class MigrateCommand implements CommandHandler {
     private final NpcStorage currentStorage;
     private final NpcStorageType currentStorageType;
     private final NpcRegistryImpl npcRegistry;
+    private final NpcSerializerRegistryImpl serializerRegistry;
 
-    public MigrateCommand(ConfigManager configManager, ZNpcsPlus plugin, PacketFactory packetFactory, ActionRegistryImpl actionRegistry, NpcTypeRegistryImpl typeRegistry, EntityPropertyRegistryImpl propertyRegistry, LegacyComponentSerializer textSerializer, NpcStorage currentStorage, NpcStorageType currentStorageType, NpcRegistryImpl npcRegistry) {
+    public MigrateCommand(ConfigManager configManager, ZNpcsPlus plugin, PacketFactory packetFactory, ActionRegistryImpl actionRegistry, NpcTypeRegistryImpl typeRegistry, EntityPropertyRegistryImpl propertyRegistry, LegacyComponentSerializer textSerializer, NpcStorage currentStorage, NpcStorageType currentStorageType, NpcRegistryImpl npcRegistry, NpcSerializerRegistryImpl serializerRegistry) {
         this.configManager = configManager;
         this.plugin = plugin;
         this.packetFactory = packetFactory;
@@ -47,6 +49,7 @@ public class MigrateCommand implements CommandHandler {
         this.currentStorage = currentStorage;
         this.currentStorageType = currentStorageType;
         this.npcRegistry = npcRegistry;
+        this.serializerRegistry = serializerRegistry;
     }
 
     @Override
@@ -63,7 +66,7 @@ public class MigrateCommand implements CommandHandler {
         if (currentStorageType == from) {
             fromStorage = currentStorage;
         } else {
-            fromStorage = from.create(configManager, plugin, packetFactory, actionRegistry, typeRegistry, propertyRegistry, textSerializer);
+            fromStorage = from.create(configManager, plugin, packetFactory, actionRegistry, typeRegistry, propertyRegistry, textSerializer, serializerRegistry);
             if (fromStorage == null) {
                 context.halt(Component.text("Failed to initialize the source storage. Please check the console for more information.", NamedTextColor.RED));
                 return;
@@ -84,7 +87,7 @@ public class MigrateCommand implements CommandHandler {
         if (currentStorageType == to) {
             toStorage = currentStorage;
         } else {
-            toStorage = to.create(configManager, plugin, packetFactory, actionRegistry, typeRegistry, propertyRegistry, textSerializer);
+            toStorage = to.create(configManager, plugin, packetFactory, actionRegistry, typeRegistry, propertyRegistry, textSerializer, serializerRegistry);
             if (toStorage == null) {
                 context.halt(Component.text("Failed to initialize the destination storage. Please check the console for more information.", NamedTextColor.RED));
                 return;
diff --git a/plugin/src/main/java/lol/pyr/znpcsplus/npc/NpcRegistryImpl.java b/plugin/src/main/java/lol/pyr/znpcsplus/npc/NpcRegistryImpl.java
index e95cecd..0dc8a1a 100644
--- a/plugin/src/main/java/lol/pyr/znpcsplus/npc/NpcRegistryImpl.java
+++ b/plugin/src/main/java/lol/pyr/znpcsplus/npc/NpcRegistryImpl.java
@@ -14,6 +14,7 @@ import lol.pyr.znpcsplus.hologram.HologramText;
 import lol.pyr.znpcsplus.interaction.ActionRegistryImpl;
 import lol.pyr.znpcsplus.packets.PacketFactory;
 import lol.pyr.znpcsplus.scheduling.TaskScheduler;
+import lol.pyr.znpcsplus.serialization.NpcSerializerRegistryImpl;
 import lol.pyr.znpcsplus.storage.NpcStorage;
 import lol.pyr.znpcsplus.storage.NpcStorageType;
 import lol.pyr.znpcsplus.util.NpcLocation;
@@ -35,13 +36,13 @@ public class NpcRegistryImpl implements NpcRegistry {
     private final Map<String, NpcEntryImpl> npcIdLookupMap = new HashMap<>();
     private final Map<UUID, NpcEntryImpl> npcUuidLookupMap = new HashMap<>();
 
-    public NpcRegistryImpl(ConfigManager configManager, ZNpcsPlus plugin, PacketFactory packetFactory, ActionRegistryImpl actionRegistry, TaskScheduler scheduler, NpcTypeRegistryImpl typeRegistry, EntityPropertyRegistryImpl propertyRegistry, LegacyComponentSerializer textSerializer) {
+    public NpcRegistryImpl(ConfigManager configManager, ZNpcsPlus plugin, PacketFactory packetFactory, ActionRegistryImpl actionRegistry, TaskScheduler scheduler, NpcTypeRegistryImpl typeRegistry, EntityPropertyRegistryImpl propertyRegistry, NpcSerializerRegistryImpl serializerRegistry, LegacyComponentSerializer textSerializer) {
         this.textSerializer = textSerializer;
         this.propertyRegistry = propertyRegistry;
-        storage = configManager.getConfig().storageType().create(configManager, plugin, packetFactory, actionRegistry, typeRegistry, propertyRegistry, textSerializer);
+        storage = configManager.getConfig().storageType().create(configManager, plugin, packetFactory, actionRegistry, typeRegistry, propertyRegistry, textSerializer, serializerRegistry);
         if (storage == null) {
             Bukkit.getLogger().warning("Failed to initialize storage, falling back to YAML");
-            storage = NpcStorageType.YAML.create(configManager, plugin, packetFactory, actionRegistry, typeRegistry, propertyRegistry, textSerializer);
+            storage = NpcStorageType.YAML.create(configManager, plugin, packetFactory, actionRegistry, typeRegistry, propertyRegistry, textSerializer, serializerRegistry);
         }
         this.packetFactory = packetFactory;
         this.configManager = configManager;
@@ -52,7 +53,13 @@ public class NpcRegistryImpl implements NpcRegistry {
         }
     }
 
+    @Override
+    public void register(NpcEntry entry) {
+        register((NpcEntryImpl) entry);
+    }
+
     private void register(NpcEntryImpl entry) {
+        if (entry == null) throw new NullPointerException();
         unregister(npcIdLookupMap.put(entry.getId(), entry));
         unregister(npcUuidLookupMap.put(entry.getNpc().getUuid(), entry));
         npcList.add(entry);
diff --git a/plugin/src/main/java/lol/pyr/znpcsplus/serialization/NpcSerializerRegistryImpl.java b/plugin/src/main/java/lol/pyr/znpcsplus/serialization/NpcSerializerRegistryImpl.java
new file mode 100644
index 0000000..71a7a95
--- /dev/null
+++ b/plugin/src/main/java/lol/pyr/znpcsplus/serialization/NpcSerializerRegistryImpl.java
@@ -0,0 +1,33 @@
+package lol.pyr.znpcsplus.serialization;
+
+import lol.pyr.znpcsplus.api.serialization.NpcSerializer;
+import lol.pyr.znpcsplus.api.serialization.NpcSerializerRegistry;
+import lol.pyr.znpcsplus.config.ConfigManager;
+import lol.pyr.znpcsplus.entity.EntityPropertyRegistryImpl;
+import lol.pyr.znpcsplus.interaction.ActionRegistryImpl;
+import lol.pyr.znpcsplus.npc.NpcTypeRegistryImpl;
+import lol.pyr.znpcsplus.packets.PacketFactory;
+import net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer;
+import org.bukkit.configuration.file.YamlConfiguration;
+
+import java.util.HashMap;
+import java.util.Map;
+
+public class NpcSerializerRegistryImpl implements NpcSerializerRegistry {
+    private final Map<Class<?>, NpcSerializer<?>> serializerMap = new HashMap<>();
+
+    public NpcSerializerRegistryImpl(PacketFactory packetFactory, ConfigManager configManager, ActionRegistryImpl actionRegistry, NpcTypeRegistryImpl typeRegistry, EntityPropertyRegistryImpl propertyRegistry, LegacyComponentSerializer textSerializer) {
+        registerSerializer(YamlConfiguration.class, new YamlSerializer(packetFactory, configManager, actionRegistry, typeRegistry, propertyRegistry, textSerializer));
+    }
+
+    @SuppressWarnings("unchecked")
+    @Override
+    public <T> NpcSerializer<T> getSerializer(Class<T> clazz) {
+        return (NpcSerializer<T>) serializerMap.get(clazz);
+    }
+
+    @Override
+    public <T> void registerSerializer(Class<T> clazz, NpcSerializer<T> serializer) {
+        serializerMap.put(clazz, serializer);
+    }
+}
diff --git a/plugin/src/main/java/lol/pyr/znpcsplus/serialization/YamlSerializer.java b/plugin/src/main/java/lol/pyr/znpcsplus/serialization/YamlSerializer.java
new file mode 100644
index 0000000..6a87564
--- /dev/null
+++ b/plugin/src/main/java/lol/pyr/znpcsplus/serialization/YamlSerializer.java
@@ -0,0 +1,154 @@
+package lol.pyr.znpcsplus.serialization;
+
+import lol.pyr.znpcsplus.api.entity.EntityProperty;
+import lol.pyr.znpcsplus.api.npc.NpcEntry;
+import lol.pyr.znpcsplus.api.serialization.NpcSerializer;
+import lol.pyr.znpcsplus.config.ConfigManager;
+import lol.pyr.znpcsplus.entity.EntityPropertyImpl;
+import lol.pyr.znpcsplus.entity.EntityPropertyRegistryImpl;
+import lol.pyr.znpcsplus.entity.PropertySerializer;
+import lol.pyr.znpcsplus.hologram.HologramImpl;
+import lol.pyr.znpcsplus.interaction.ActionRegistryImpl;
+import lol.pyr.znpcsplus.npc.NpcEntryImpl;
+import lol.pyr.znpcsplus.npc.NpcImpl;
+import lol.pyr.znpcsplus.npc.NpcTypeRegistryImpl;
+import lol.pyr.znpcsplus.packets.PacketFactory;
+import lol.pyr.znpcsplus.util.NpcLocation;
+import net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer;
+import org.bukkit.Bukkit;
+import org.bukkit.configuration.ConfigurationSection;
+import org.bukkit.configuration.file.YamlConfiguration;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Objects;
+import java.util.UUID;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+import java.util.stream.Collectors;
+
+public class YamlSerializer implements NpcSerializer<YamlConfiguration> {
+    private final static Logger logger = Logger.getLogger("YamlSerializer");
+
+    private final PacketFactory packetFactory;
+    private final ConfigManager configManager;
+    private final ActionRegistryImpl actionRegistry;
+    private final NpcTypeRegistryImpl typeRegistry;
+    private final EntityPropertyRegistryImpl propertyRegistry;
+    private final LegacyComponentSerializer textSerializer;
+
+    public YamlSerializer(PacketFactory packetFactory, ConfigManager configManager, ActionRegistryImpl actionRegistry, NpcTypeRegistryImpl typeRegistry, EntityPropertyRegistryImpl propertyRegistry, LegacyComponentSerializer textSerializer) {
+        this.packetFactory = packetFactory;
+        this.configManager = configManager;
+        this.actionRegistry = actionRegistry;
+        this.typeRegistry = typeRegistry;
+        this.propertyRegistry = propertyRegistry;
+        this.textSerializer = textSerializer;
+    }
+
+    @Override
+    public YamlConfiguration serialize(NpcEntry entry) {
+        YamlConfiguration config = new YamlConfiguration();
+        config.set("id", entry.getId());
+        config.set("is-processed", entry.isProcessed());
+        config.set("allow-commands", entry.isAllowCommandModification());
+        config.set("save", entry.isSave());
+
+        NpcImpl npc = (NpcImpl) entry.getNpc();
+        config.set("enabled", npc.isEnabled());
+        config.set("uuid", npc.getUuid().toString());
+        config.set("world", npc.getWorldName());
+        config.set("location", serializeLocation(npc.getLocation()));
+        config.set("type", npc.getType().getName());
+
+        for (EntityProperty<?> property : npc.getAllProperties()) try {
+            PropertySerializer<?> serializer = propertyRegistry.getSerializer(((EntityPropertyImpl<?>) property).getType());
+            if (serializer == null) {
+                Bukkit.getLogger().log(Level.WARNING, "Unknown serializer for property '" + property.getName() + "' for npc '" + entry.getId() + "'. skipping ...");
+                continue;
+            }
+            config.set("properties." + property.getName(), serializer.UNSAFE_serialize(npc.getProperty(property)));
+        } catch (Exception exception) {
+            logger.severe("Failed to serialize property " + property.getName() + " for npc with id " + entry.getId());
+            exception.printStackTrace();
+        }
+
+        HologramImpl hologram = npc.getHologram();
+        if (hologram.getOffset() != 0.0) config.set("hologram.offset", hologram.getOffset());
+        if (hologram.getRefreshDelay() != -1) config.set("hologram.refresh-delay", hologram.getRefreshDelay());
+        List<String> lines = new ArrayList<>(npc.getHologram().getLines().size());
+        for (int i = 0; i < hologram.getLines().size(); i++) {
+            lines.add(hologram.getLine(i));
+        }
+        config.set("hologram.lines", lines);
+        config.set("actions", npc.getActions().stream()
+                .map(actionRegistry::serialize)
+                .filter(Objects::nonNull)
+                .collect(Collectors.toList()));
+
+        return config;
+    }
+
+    @Override
+    public NpcEntry deserialize(YamlConfiguration config) {
+        UUID uuid = config.contains("uuid") ? UUID.fromString(config.getString("uuid")) : UUID.randomUUID();
+        NpcImpl npc = new NpcImpl(uuid, propertyRegistry, configManager, packetFactory, textSerializer, config.getString("world"),
+                typeRegistry.getByName(config.getString("type")), deserializeLocation(config.getConfigurationSection("location")));
+
+        if (config.isBoolean("enabled")) npc.setEnabled(config.getBoolean("enabled"));
+
+        ConfigurationSection properties = config.getConfigurationSection("properties");
+        if (properties != null) {
+            for (String key : properties.getKeys(false)) {
+                EntityPropertyImpl<?> property = propertyRegistry.getByName(key);
+                if (property == null) {
+                    Bukkit.getLogger().log(Level.WARNING, "Unknown property '" + key + "' for npc '" + config.getString("id") + "'. skipping ...");
+                    continue;
+                }
+                PropertySerializer<?> serializer = propertyRegistry.getSerializer(property.getType());
+                if (serializer == null) {
+                    Bukkit.getLogger().log(Level.WARNING, "Unknown serializer for property '" + key + "' for npc '" + config.getString("id") + "'. skipping ...");
+                    continue;
+                }
+                Object value = serializer.deserialize(properties.getString(key));
+                if (value == null) {
+                    Bukkit.getLogger().log(Level.WARNING, "Failed to deserialize property '" + key + "' for npc '" + config.getString("id") + "'. Resetting to default ...");
+                    value = property.getDefaultValue();
+                }
+                npc.UNSAFE_setProperty(property, value);
+            }
+        }
+        HologramImpl hologram = npc.getHologram();
+        hologram.setOffset(config.getDouble("hologram.offset", 0.0));
+        hologram.setRefreshDelay(config.getLong("hologram.refresh-delay", -1));
+        for (String line : config.getStringList("hologram.lines")) hologram.addLine(line);
+        for (String s : config.getStringList("actions")) npc.addAction(actionRegistry.deserialize(s));
+
+        NpcEntryImpl entry = new NpcEntryImpl(config.getString("id"), npc);
+        entry.setProcessed(config.getBoolean("is-processed"));
+        entry.setAllowCommandModification(config.getBoolean("allow-commands"));
+        entry.setSave(config.getBoolean("save"));
+
+        return entry;
+    }
+
+    public NpcLocation deserializeLocation(ConfigurationSection section) {
+        return new NpcLocation(
+                section.getDouble("x"),
+                section.getDouble("y"),
+                section.getDouble("z"),
+                (float) section.getDouble("yaw"),
+                (float) section.getDouble("pitch")
+        );
+    }
+
+    public YamlConfiguration serializeLocation(NpcLocation location) {
+        YamlConfiguration config = new YamlConfiguration();
+        config.set("x", location.getX());
+        config.set("y", location.getY());
+        config.set("z", location.getZ());
+        config.set("yaw", location.getYaw());
+        config.set("pitch", location.getPitch());
+        return config;
+    }
+}
diff --git a/plugin/src/main/java/lol/pyr/znpcsplus/storage/NpcStorageType.java b/plugin/src/main/java/lol/pyr/znpcsplus/storage/NpcStorageType.java
index 5205dfc..b4139cc 100644
--- a/plugin/src/main/java/lol/pyr/znpcsplus/storage/NpcStorageType.java
+++ b/plugin/src/main/java/lol/pyr/znpcsplus/storage/NpcStorageType.java
@@ -6,6 +6,7 @@ import lol.pyr.znpcsplus.entity.EntityPropertyRegistryImpl;
 import lol.pyr.znpcsplus.interaction.ActionRegistryImpl;
 import lol.pyr.znpcsplus.npc.NpcTypeRegistryImpl;
 import lol.pyr.znpcsplus.packets.PacketFactory;
+import lol.pyr.znpcsplus.serialization.NpcSerializerRegistryImpl;
 import lol.pyr.znpcsplus.storage.mysql.MySQLStorage;
 import lol.pyr.znpcsplus.storage.sqlite.SQLiteStorage;
 import lol.pyr.znpcsplus.storage.yaml.YamlStorage;
@@ -16,13 +17,13 @@ import java.io.File;
 public enum NpcStorageType {
     YAML {
         @Override
-        public NpcStorage create(ConfigManager configManager, ZNpcsPlus plugin, PacketFactory packetFactory, ActionRegistryImpl actionRegistry, NpcTypeRegistryImpl typeRegistry, EntityPropertyRegistryImpl propertyRegistry, LegacyComponentSerializer textSerializer) {
-            return new YamlStorage(packetFactory, configManager, actionRegistry, typeRegistry, propertyRegistry, textSerializer, new File(plugin.getDataFolder(), "data"));
+        public NpcStorage create(ConfigManager configManager, ZNpcsPlus plugin, PacketFactory packetFactory, ActionRegistryImpl actionRegistry, NpcTypeRegistryImpl typeRegistry, EntityPropertyRegistryImpl propertyRegistry, LegacyComponentSerializer textSerializer, NpcSerializerRegistryImpl serializerRegistry) {
+            return new YamlStorage(serializerRegistry, new File(plugin.getDataFolder(), "data"));
         }
     },
     SQLITE {
         @Override
-        public NpcStorage create(ConfigManager configManager, ZNpcsPlus plugin, PacketFactory packetFactory, ActionRegistryImpl actionRegistry, NpcTypeRegistryImpl typeRegistry, EntityPropertyRegistryImpl propertyRegistry, LegacyComponentSerializer textSerializer) {
+        public NpcStorage create(ConfigManager configManager, ZNpcsPlus plugin, PacketFactory packetFactory, ActionRegistryImpl actionRegistry, NpcTypeRegistryImpl typeRegistry, EntityPropertyRegistryImpl propertyRegistry, LegacyComponentSerializer textSerializer, NpcSerializerRegistryImpl serializerRegistry) {
             try {
                 return new SQLiteStorage(packetFactory, configManager, actionRegistry, typeRegistry, propertyRegistry, textSerializer, new File(plugin.getDataFolder(), "znpcsplus.sqlite"));
             } catch (Exception e) {
@@ -33,7 +34,7 @@ public enum NpcStorageType {
     },
     MYSQL {
         @Override
-        public NpcStorage create(ConfigManager configManager, ZNpcsPlus plugin, PacketFactory packetFactory, ActionRegistryImpl actionRegistry, NpcTypeRegistryImpl typeRegistry, EntityPropertyRegistryImpl propertyRegistry, LegacyComponentSerializer textSerializer) {
+        public NpcStorage create(ConfigManager configManager, ZNpcsPlus plugin, PacketFactory packetFactory, ActionRegistryImpl actionRegistry, NpcTypeRegistryImpl typeRegistry, EntityPropertyRegistryImpl propertyRegistry, LegacyComponentSerializer textSerializer, NpcSerializerRegistryImpl serializerRegistry) {
             try {
                 return new MySQLStorage(packetFactory, configManager, actionRegistry, typeRegistry, propertyRegistry, textSerializer);
             } catch (Exception e) {
@@ -43,5 +44,5 @@ public enum NpcStorageType {
         }
     };
 
-    public abstract NpcStorage create(ConfigManager configManager, ZNpcsPlus plugin, PacketFactory packetFactory, ActionRegistryImpl actionRegistry, NpcTypeRegistryImpl typeRegistry, EntityPropertyRegistryImpl propertyRegistry, LegacyComponentSerializer textSerializer);
+    public abstract NpcStorage create(ConfigManager configManager, ZNpcsPlus plugin, PacketFactory packetFactory, ActionRegistryImpl actionRegistry, NpcTypeRegistryImpl typeRegistry, EntityPropertyRegistryImpl propertyRegistry, LegacyComponentSerializer textSerializer, NpcSerializerRegistryImpl serializerRegistry);
 }
diff --git a/plugin/src/main/java/lol/pyr/znpcsplus/storage/yaml/YamlStorage.java b/plugin/src/main/java/lol/pyr/znpcsplus/storage/yaml/YamlStorage.java
index 55e46dd..b985fa7 100644
--- a/plugin/src/main/java/lol/pyr/znpcsplus/storage/yaml/YamlStorage.java
+++ b/plugin/src/main/java/lol/pyr/znpcsplus/storage/yaml/YamlStorage.java
@@ -1,47 +1,28 @@
 package lol.pyr.znpcsplus.storage.yaml;
 
-import lol.pyr.znpcsplus.api.entity.EntityProperty;
-import lol.pyr.znpcsplus.config.ConfigManager;
-import lol.pyr.znpcsplus.entity.EntityPropertyImpl;
-import lol.pyr.znpcsplus.entity.EntityPropertyRegistryImpl;
-import lol.pyr.znpcsplus.entity.PropertySerializer;
-import lol.pyr.znpcsplus.hologram.HologramImpl;
-import lol.pyr.znpcsplus.interaction.ActionRegistryImpl;
+import lol.pyr.znpcsplus.api.serialization.NpcSerializer;
 import lol.pyr.znpcsplus.npc.NpcEntryImpl;
-import lol.pyr.znpcsplus.npc.NpcImpl;
-import lol.pyr.znpcsplus.npc.NpcTypeRegistryImpl;
-import lol.pyr.znpcsplus.packets.PacketFactory;
+import lol.pyr.znpcsplus.serialization.NpcSerializerRegistryImpl;
 import lol.pyr.znpcsplus.storage.NpcStorage;
 import lol.pyr.znpcsplus.util.NpcLocation;
-import net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer;
-import org.bukkit.Bukkit;
 import org.bukkit.configuration.ConfigurationSection;
 import org.bukkit.configuration.file.YamlConfiguration;
 
 import java.io.File;
-import java.util.*;
-import java.util.logging.Level;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
 import java.util.logging.Logger;
-import java.util.stream.Collectors;
 
 public class YamlStorage implements NpcStorage {
     private final static Logger logger = Logger.getLogger("YamlStorage");
 
-    private final PacketFactory packetFactory;
-    private final ConfigManager configManager;
-    private final ActionRegistryImpl actionRegistry;
-    private final NpcTypeRegistryImpl typeRegistry;
-    private final EntityPropertyRegistryImpl propertyRegistry;
-    private final LegacyComponentSerializer textSerializer;
     private final File folder;
+    private final NpcSerializer<YamlConfiguration> yamlSerializer;
 
-    public YamlStorage(PacketFactory packetFactory, ConfigManager configManager, ActionRegistryImpl actionRegistry, NpcTypeRegistryImpl typeRegistry, EntityPropertyRegistryImpl propertyRegistry, LegacyComponentSerializer textSerializer, File folder) {
-        this.packetFactory = packetFactory;
-        this.configManager = configManager;
-        this.actionRegistry = actionRegistry;
-        this.typeRegistry = typeRegistry;
-        this.propertyRegistry = propertyRegistry;
-        this.textSerializer = textSerializer;
+    public YamlStorage(NpcSerializerRegistryImpl serializerRegistry, File folder) {
+        this.yamlSerializer = serializerRegistry.getSerializer(YamlConfiguration.class);
         this.folder = folder;
         if (!this.folder.exists()) this.folder.mkdirs();
     }
@@ -53,45 +34,7 @@ public class YamlStorage implements NpcStorage {
         List<NpcEntryImpl> npcs = new ArrayList<>(files.length);
         for (File file : files) if (file.isFile() && file.getName().toLowerCase().endsWith(".yml")) try {
             YamlConfiguration config = YamlConfiguration.loadConfiguration(file);
-            UUID uuid = config.contains("uuid") ? UUID.fromString(config.getString("uuid")) : UUID.randomUUID();
-            NpcImpl npc = new NpcImpl(uuid, propertyRegistry, configManager, packetFactory, textSerializer, config.getString("world"),
-                    typeRegistry.getByName(config.getString("type")), deserializeLocation(config.getConfigurationSection("location")));
-
-            if (config.isBoolean("enabled")) npc.setEnabled(config.getBoolean("enabled"));
-
-            ConfigurationSection properties = config.getConfigurationSection("properties");
-            if (properties != null) {
-                for (String key : properties.getKeys(false)) {
-                    EntityPropertyImpl<?> property = propertyRegistry.getByName(key);
-                    if (property == null) {
-                        Bukkit.getLogger().log(Level.WARNING, "Unknown property '" + key + "' for npc '" + config.getString("id") + "'. skipping ...");
-                        continue;
-                    }
-                    PropertySerializer<?> serializer = propertyRegistry.getSerializer(property.getType());
-                    if (serializer == null) {
-                        Bukkit.getLogger().log(Level.WARNING, "Unknown serializer for property '" + key + "' for npc '" + config.getString("id") + "'. skipping ...");
-                        continue;
-                    }
-                    Object value = serializer.deserialize(properties.getString(key));
-                    if (value == null) {
-                        Bukkit.getLogger().log(Level.WARNING, "Failed to deserialize property '" + key + "' for npc '" + config.getString("id") + "'. Resetting to default ...");
-                        value = property.getDefaultValue();
-                    }
-                    npc.UNSAFE_setProperty(property, value);
-                }
-            }
-            HologramImpl hologram = npc.getHologram();
-            hologram.setOffset(config.getDouble("hologram.offset", 0.0));
-            hologram.setRefreshDelay(config.getLong("hologram.refresh-delay", -1));
-            for (String line : config.getStringList("hologram.lines")) hologram.addLine(line);
-            for (String s : config.getStringList("actions")) npc.addAction(actionRegistry.deserialize(s));
-
-            NpcEntryImpl entry = new NpcEntryImpl(config.getString("id"), npc);
-            entry.setProcessed(config.getBoolean("is-processed"));
-            entry.setAllowCommandModification(config.getBoolean("allow-commands"));
-            entry.setSave(true);
-
-            npcs.add(entry);
+            npcs.add((NpcEntryImpl) yamlSerializer.deserialize(config));
         } catch (Throwable t) {
             logger.severe("Failed to load npc file: " + file.getName());
             t.printStackTrace();
@@ -102,43 +45,7 @@ public class YamlStorage implements NpcStorage {
     @Override
     public void saveNpcs(Collection<NpcEntryImpl> npcs) {
         for (NpcEntryImpl entry : npcs) try {
-            YamlConfiguration config = new YamlConfiguration();
-            config.set("id", entry.getId());
-            config.set("is-processed", entry.isProcessed());
-            config.set("allow-commands", entry.isAllowCommandModification());
-
-            NpcImpl npc = entry.getNpc();
-            config.set("enabled", npc.isEnabled());
-            config.set("uuid", npc.getUuid().toString());
-            config.set("world", npc.getWorldName());
-            config.set("location", serializeLocation(npc.getLocation()));
-            config.set("type", npc.getType().getName());
-
-            for (EntityProperty<?> property : npc.getAllProperties()) try {
-                PropertySerializer<?> serializer = propertyRegistry.getSerializer(((EntityPropertyImpl<?>) property).getType());
-                if (serializer == null) {
-                    Bukkit.getLogger().log(Level.WARNING, "Unknown serializer for property '" + property.getName() + "' for npc '" + entry.getId() + "'. skipping ...");
-                    continue;
-                }
-                config.set("properties." + property.getName(), serializer.UNSAFE_serialize(npc.getProperty(property)));
-            } catch (Exception exception) {
-                logger.severe("Failed to serialize property " + property.getName() + " for npc with id " + entry.getId());
-                exception.printStackTrace();
-            }
-
-            HologramImpl hologram = npc.getHologram();
-            if (hologram.getOffset() != 0.0) config.set("hologram.offset", hologram.getOffset());
-            if (hologram.getRefreshDelay() != -1) config.set("hologram.refresh-delay", hologram.getRefreshDelay());
-            List<String> lines = new ArrayList<>(npc.getHologram().getLines().size());
-            for (int i = 0; i < hologram.getLines().size(); i++) {
-                lines.add(hologram.getLine(i));
-            }
-            config.set("hologram.lines", lines);
-            config.set("actions", npc.getActions().stream()
-                    .map(actionRegistry::serialize)
-                    .filter(Objects::nonNull)
-                    .collect(Collectors.toList()));
-
+            YamlConfiguration config = yamlSerializer.serialize(entry);
             config.save(fileFor(entry));
         } catch (Exception exception) {
             logger.severe("Failed to save npc with id " + entry.getId());