{"id":35694437,"url":"https://github.com/thenextlvl-net/nbt","last_synced_at":"2026-05-24T10:01:01.473Z","repository":{"id":313373426,"uuid":"1051159043","full_name":"TheNextLvl-net/NBT","owner":"TheNextLvl-net","description":"A simple library to read and write NBT files","archived":false,"fork":false,"pushed_at":"2026-04-22T03:47:02.000Z","size":617,"stargazers_count":4,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"main","last_synced_at":"2026-04-22T05:41:44.828Z","etag":null,"topics":["minecraft-nbt","nbt","nbt-api","nbt-files","nbt-format","nbt-library","nbt-parser"],"latest_commit_sha":null,"homepage":"","language":"Java","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"gpl-3.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/TheNextLvl-net.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null},"funding":{"github":["TheNextLvl-net","NonSwag"],"patreon":null,"open_collective":null,"ko_fi":null,"tidelift":null,"community_bridge":null,"liberapay":null,"issuehunt":null,"lfx_crowdfunding":null,"polar":null,"buy_me_a_coffee":null,"thanks_dev":null,"custom":null}},"created_at":"2025-09-05T14:31:24.000Z","updated_at":"2026-04-22T03:47:04.000Z","dependencies_parsed_at":"2026-01-21T13:02:03.548Z","dependency_job_id":null,"html_url":"https://github.com/TheNextLvl-net/NBT","commit_stats":null,"previous_names":["thenextlvl-net/nbt"],"tags_count":20,"template":false,"template_full_name":null,"purl":"pkg:github/TheNextLvl-net/NBT","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/TheNextLvl-net%2FNBT","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/TheNextLvl-net%2FNBT/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/TheNextLvl-net%2FNBT/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/TheNextLvl-net%2FNBT/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/TheNextLvl-net","download_url":"https://codeload.github.com/TheNextLvl-net/NBT/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/TheNextLvl-net%2FNBT/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":33429192,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-23T22:14:44.296Z","status":"online","status_checked_at":"2026-05-24T02:00:06.296Z","response_time":57,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["minecraft-nbt","nbt","nbt-api","nbt-files","nbt-format","nbt-library","nbt-parser"],"created_at":"2026-01-06T00:13:33.288Z","updated_at":"2026-05-24T10:01:01.466Z","avatar_url":"https://github.com/TheNextLvl-net.png","language":"Java","funding_links":["https://github.com/sponsors/TheNextLvl-net","https://github.com/sponsors/NonSwag"],"categories":[],"sub_categories":[],"readme":"# NBT\n\nA small library for reading, writing, and (de)serializing Minecraft-like NBT (Named Binary Tag) data.\n\nThis project provides:\n\n- Low-level streaming APIs to read/write NBT with GZIP compression: NBTInputStream and NBTOutputStream.\n- A convenient file wrapper NBTFile for loading/saving a CompoundTag from/to disk.\n- A flexible, pluggable serialization system (NBT facade) to convert between Java objects and Tag trees using\n  serializers/deserializers (aka adapters).\n\n## Installation\n\nGradle (Kotlin DSL):\n\n- Repository is published to https://repo.thenextlvl.net/#/releases/net/thenextlvl/nbt\n- Group/artifact inferred from `build.gradle.kts`\n\n```kts\nrepositories {\n    mavenCentral()\n    maven(\"https://repo.thenextlvl.net/releases/\")\n}\n\ndependencies {\n    implementation(\"net.thenextlvl:nbt:3.0.0\")\n}\n```\n\n## Core concepts\n\n- Tag: Base type for all NBT values (ByteTag, ShortTag, IntTag, LongTag, FloatTag, DoubleTag, StringTag, ByteArrayTag,\n  IntArrayTag, LongArrayTag, ListTag, CompoundTag). All tags know how to read/write themselves from/to streams.\n- CompoundTag: A map of name → Tag. Commonly used as the root tag in files.\n- NBTInputStream / NBTOutputStream: Low-level, GZIP-compressed streams for reading/writing tags. Strings are encoded\n  using the configured Charset (UTF-8 by default).\n- NBTFile: Small utility to load/save a CompoundTag from a file path with charset handling.\n- Serialization API: The NBT interface that converts between Tag and arbitrary Java objects using TagSerializer and\n  TagDeserializer (or a combined TagAdapter).\n\n## Reading NBT files\n\nYou can read any NBT file using NBTInputStream. The stream transparently handles GZIP compression.\n\n```java\nimport net.thenextlvl.nbt.NBTInputStream;\nimport net.thenextlvl.nbt.tag.CompoundTag;\nimport net.thenextlvl.nbt.tag.Tag;\n\nimport javax.swing.text.html.Option;\nimport java.io.FileInputStream;\nimport java.util.Map;\nimport java.util.Optional;\n\npublic class NBTExample {\n    public static void readData() throws Exception {\n        try (NBTInputStream input = new NBTInputStream(new FileInputStream(\"data.nbt\"))) {\n            // Read the root entry (tag and optional name)\n            Map.Entry\u003cTag, Optional\u003cString\u003e\u003e entry = input.readNamedTag();\n            Tag root = entry.getKey();\n            String rootName = entry.getValue().orElse(null);\n\n            if (root instanceof CompoundTag compound) {\n                // Access values by name\n                var level = compound.get(\"Level\");\n                var data = compound.getAsCompound(\"Data\");\n                var list = compound.getAsList(\"Items\");\n            }\n        }\n    }\n}\n```\n\n\u003e [!TIP]\n\u003e - If you only need the tag, use readTag().\n\u003e - Unknown tag IDs cause an IllegalArgumentException. You may register custom mappings on NBTInputStream via\n    registerMapping(typeId, function).\n\n## Writing NBT files\n\nUse NBTOutputStream to write a named root tag. The stream writes GZIP-compressed output.\n\n```java\nimport net.thenextlvl.nbt.NBTOutputStream;\nimport net.thenextlvl.nbt.tag.CompoundTag;\n\nimport java.io.FileOutputStream;\n\npublic class NBTExample {\n\n    public static void writeData() throws Exception {\n        try (var out = new NBTOutputStream(new FileOutputStream(\"data.nbt\"))) {\n            CompoundTag root = CompoundTag.builder()\n                    .put(\"Name\", \"Example\")\n                    .put(\"Health\", 20)\n                    .put(\"Position\", CompoundTag.builder()\n                            .put(\"x\", 1)\n                            .put(\"y\", 64)\n                            .put(\"z\", 1)\n                            .build())\n                    .build();\n            out.writeTag(\"Root\", root); // name can be null\n        }\n    }\n}\n```\n\nCompoundTag has a fluent Builder for convenience.\n\n## Using NBTFile helper\n\nIf you prefer a small wrapper for file IO, NBTFile\u003cR extends CompoundTag\u003e can load/save and retain the root name.\n\n```java\nimport core.io.PathIO; // from net.thenextlvl.core:files\nimport net.thenextlvl.nbt.file.NBTFile;\nimport net.thenextlvl.nbt.tag.CompoundTag;\n\npublic static class NBTExample {\n\n    public static void writeData() throws Exception {\n        NBTFile\u003cCompoundTag\u003e file = new NBTFile\u003c\u003e(new PathIO(Path.of(\"data.nbt\")), CompoundTag.empty());\n        CompoundTag root = file.get(); // loads if file exists, otherwise returns default root\n        String rootName = file.getRootName().orElse(null);\n\n        // modify root ...\n        root.add(\"Updated\", true);\n        file.setRootName(\"Root\");\n        file.save();\n    }\n}\n```\n\n## Serialization: NBT facade\n\nThe serialization API turns Java objects into Tags and back. The NBT interface is the entry point. You configure an\ninstance via `NBT.builder()` and register (de)serializers or combined adapters.\n\nKey types:\n\n- `TagSerializer\u003cT\u003e`: object -\u003e Tag\n- `TagDeserializer\u003cT\u003e`: Tag -\u003e object\n- `TagAdapter\u003cT\u003e`: both serializer and deserializer in one\n- `TagSerializationContext` / `TagDeserializationContext`: provided to your (de)serializers for recursive (de)\n  serialization\n\nAn `NBT` instance comes with built-in adapters for common types:\n\n- Primitives and boxed: boolean/Boolean, byte/Byte, short/Short, int/Integer, long/Long, float/Float, double/Double\n- String, java.io.File, java.nio.file.Path, java.time.Duration, java.net.InetSocketAddress, java.util.UUID\n\n### Quick start\n\n```java\nimport net.thenextlvl.nbt.serialization.NBT;\nimport net.thenextlvl.nbt.tag.CompoundTag;\nimport net.thenextlvl.nbt.tag.Tag;\n\npublic record Player(String name, int level) {\n\n    public static void adapt() {\n        var nbt = NBT.builder().registerTypeAdapter(Player.class, new TagAdapter\u003cPlayer\u003e() {\n            @Override\n            public Tag serialize(Player player, TagSerializationContext ctx) {\n                return CompoundTag.builder()\n                        .put(\"name\", player.name())\n                        .put(\"level\", player.level())\n                        .build();\n            }\n\n            @Override\n            public Player deserialize(Tag tag, TagDeserializationContext ctx) {\n                var root = tag.getAsCompound();\n                var name = root.get(\"name\").getAsString();\n                var level = root.get(\"level\").getAsInt();\n                return new Player(name, level);\n            }\n        }).build();\n\n        Tag asTag = nbt.serialize(new Player(\"Alex\", 42));\n        Player back = nbt.deserialize(asTag, Player.class);\n    }\n}\n```\n\nYou can also register serializer and deserializer separately:\n\n```java\nNBT nbt = NBT.builder()\n        .registerTypeAdapter(Player.class, (TagSerializer\u003cPlayer\u003e) (player, context) -\u003e {\n            return CompoundTag.builder()\n                    .put(\"name\", player.name())\n                    .put(\"level\", player.level())\n                    .build();\n        })\n        .registerTypeAdapter(Player.class, (TagDeserializer\u003cPlayer\u003e) (tag, context) -\u003e {\n            var root = tag.getAsCompound();\n            return new Player(\n                    root.get(\"name\").getAsString(),\n                    root.get(\"level\").getAsInt()\n            );\n        })\n        .build();\n```\n\nIf you need polymorphic handling, register a hierarchy adapter so it also applies to subtypes:\n\n```java\nNBT nbt = NBT.builder().registerTypeHierarchyAdapter(Animal.class, new AnimalAdapter()).build(); // applies to all subclasses\n```\n\nDuring (de)serialization, you can call `context.serialize(object)` and `context.deserialize(tag, type)` from within your\ncustom adapters to handle nested fields using already registered adapters.\n\n## Creating a custom serializer with the NBT class\n\nThis example shows how to write a dedicated adapter for a complex type containing nested objects and collections.\n\n```java\nimport net.thenextlvl.nbt.serialization.*;\nimport net.thenextlvl.nbt.tag.*;\n\nimport java.util.List;\n\nrecord Position(int x, int y, int z) {\n}\n\nrecord InventoryItem(String id, int count) {\n}\n\nrecord PlayerData(String name, Position pos, java.util.List\u003cInventoryItem\u003e items) {\n}\n\nclass PositionAdapter implements TagAdapter\u003cPosition\u003e {\n    @Override\n    public Tag serialize(Position position, TagSerializationContext context) {\n        return CompoundTag.builder()\n                .put(\"x\", position.x())\n                .put(\"y\", position.y())\n                .put(\"z\", position.z())\n                .build();\n    }\n\n    @Override\n    public Position deserialize(Tag tag, TagDeserializationContext context) {\n        var root = tag.getAsCompound();\n        return new Position(\n                root.get(\"x\").getAsInt(),\n                root.get(\"y\").getAsInt(),\n                root.get(\"z\").getAsInt()\n        );\n    }\n}\n\nclass InventoryItemAdapter implements TagAdapter\u003cInventoryItem\u003e {\n    @Override\n    public Tag serialize(InventoryItem item, TagSerializationContext context) {\n        return CompoundTag.builder()\n                .put(\"id\", item.id())\n                .put(\"count\", item.count())\n                .build();\n    }\n\n    @Override\n    public InventoryItem deserialize(Tag tag, TagDeserializationContext context) {\n        var root = tag.getAsCompound();\n        return new InventoryItem(\n                root.get(\"id\").getAsString(),\n                root.get(\"count\").getAsInt()\n        );\n    }\n}\n\nclass PlayerDataAdapter implements TagAdapter\u003cPlayerData\u003e {\n    @Override\n    public Tag serialize(PlayerData data, TagSerializationContext context) throws ParserException {\n        var builder = ListTag.builder()\n                .contentType(CompoundTag.ID);\n        for (var it : data.items()) {\n            // Let context use InventoryItemAdapter\n            builder.add(context.serialize(it));\n        }\n        return CompoundTag.builder()\n                .put(\"name\", data.name())\n                .put(\"pos\", context.serialize(data.pos())) // delegate to PositionAdapter\n                .put(\"items\", builder.build())\n                .build();\n    }\n\n    @Override\n    public PlayerData deserialize(Tag tag, TagDeserializationContext context) throws ParserException {\n        var c = tag.getAsCompound();\n        var name = c.get(\"name\").getAsString();\n        var pos = context.deserialize(c.get(\"pos\"), Position.class);\n        var listTag = c.getAsList(\"items\");\n        final var items = new java.util.ArrayList\u003cInventoryItem\u003e(listTag.size());\n        for (final var t : listTag) {\n            items.add(context.deserialize(t, InventoryItem.class));\n        }\n        return new PlayerData(name, pos, List.copyOf(items));\n    }\n}\n\nvar nbt = NBT.builder()\n        .registerTypeAdapter(Position.class, new PositionAdapter())\n        .registerTypeAdapter(InventoryItem.class, new InventoryItemAdapter())\n        .registerTypeAdapter(PlayerData.class, new PlayerDataAdapter())\n        .build();\n\nvar data = new PlayerData(\"Alex\", new Position(1, 64, 1), List.of(new InventoryItem(\"minecraft:stone\", 32)));\nTag tag = nbt.serialize(data);\nPlayerData back = nbt.deserialize(tag, PlayerData.class);\n```\n\n\u003e [!TIP]\n\u003e - Use `CompoundTag.Builder` to construct compound values fluently.\n\u003e - `ListTag\u003cE extends Tag\u003e` stores tags only; use the context to convert elements.\n\u003e - Throw `ParserException` in your (de)serializers to signal invalid data.\n\n## Registering custom tag type mappings for reading\n\nIf you introduce your own `Tag` implementation with a custom type ID, you can teach NBTInputStream how to read it:\n\n```java\npublic static void createCustomTag() throws Exception {\n    final NBTInputStream input = new NBTInputStream(new FileInputStream(\"data.nbt\"));\n    input.registerMapping(MyCustomTag.ID, MyCustomTag::read);\n}\n```\n\n`NBTOutputStream` will call `Tag#write` on whatever Tag you pass to `writeTag`.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fthenextlvl-net%2Fnbt","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fthenextlvl-net%2Fnbt","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fthenextlvl-net%2Fnbt/lists"}