public SkeletonData readSkeletonData(FileHandle file) {
    if (file == null) throw new IllegalArgumentException("file cannot be null.");

    SkeletonData skeletonData = new SkeletonData();
    skeletonData.setName(file.nameWithoutExtension());

    JsonValue root = new JsonReader().parse(file);

    // Bones.
    for (JsonValue boneMap = root.getChild("bones"); boneMap != null; boneMap = boneMap.next()) {
      BoneData parent = null;
      String parentName = boneMap.getString("parent", null);
      if (parentName != null) {
        parent = skeletonData.findBone(parentName);
        if (parent == null)
          throw new SerializationException("Parent bone not found: " + parentName);
      }
      BoneData boneData = new BoneData(boneMap.getString("name"), parent);
      boneData.length = boneMap.getFloat("length", 0) * scale;
      boneData.x = boneMap.getFloat("x", 0) * scale;
      boneData.y = boneMap.getFloat("y", 0) * scale;
      boneData.rotation = boneMap.getFloat("rotation", 0);
      boneData.scaleX = boneMap.getFloat("scaleX", 1);
      boneData.scaleY = boneMap.getFloat("scaleY", 1);
      boneData.inheritScale = boneMap.getBoolean("inheritScale", true);
      boneData.inheritRotation = boneMap.getBoolean("inheritRotation", true);
      skeletonData.addBone(boneData);
    }

    // Slots.
    for (JsonValue slotMap = root.getChild("slots"); slotMap != null; slotMap = slotMap.next()) {
      String slotName = slotMap.getString("name");
      String boneName = slotMap.getString("bone");
      BoneData boneData = skeletonData.findBone(boneName);
      if (boneData == null) throw new SerializationException("Slot bone not found: " + boneName);
      SlotData slotData = new SlotData(slotName, boneData);

      String color = slotMap.getString("color", null);
      if (color != null) slotData.getColor().set(Color.valueOf(color));

      slotData.setAttachmentName(slotMap.getString("attachment", null));

      slotData.additiveBlending = slotMap.getBoolean("additive", false);

      skeletonData.addSlot(slotData);
    }

    // Skins.
    for (JsonValue skinMap = root.getChild("skins"); skinMap != null; skinMap = skinMap.next()) {
      Skin skin = new Skin(skinMap.name());
      for (JsonValue slotEntry = skinMap.child(); slotEntry != null; slotEntry = slotEntry.next()) {
        int slotIndex = skeletonData.findSlotIndex(slotEntry.name());
        for (JsonValue entry = slotEntry.child(); entry != null; entry = entry.next()) {
          Attachment attachment = readAttachment(skin, entry.name(), entry);
          if (attachment != null) skin.addAttachment(slotIndex, entry.name(), attachment);
        }
      }
      skeletonData.addSkin(skin);
      if (skin.name.equals("default")) skeletonData.setDefaultSkin(skin);
    }

    // Animations.
    for (JsonValue animationMap = root.getChild("animations");
        animationMap != null;
        animationMap = animationMap.next())
      readAnimation(animationMap.name(), animationMap, skeletonData);

    skeletonData.bones.shrink();
    skeletonData.slots.shrink();
    skeletonData.skins.shrink();
    skeletonData.animations.shrink();
    return skeletonData;
  }