NiklasEi / bevy_common_assets

Collection of generic asset loaders for common file formats
Apache License 2.0
198 stars 24 forks source link

Try to load JSON-File: "Could not parse the JSON: invalid type: map" #38

Open gkvoelkl opened 4 months ago

gkvoelkl commented 4 months ago

Hello,

I try to load this JSON file:

[
    {
        "name": "Foyer",
        "x": 0,
        "y": 0,
        "width": 3,
        "height": 3,
        "color": "lightgray",
        "description": "Ein großes, einladendes Foyer mit Marmorboden, hohen Decken und einem majestätischen Kronleuchter. An den Wänden hängen alte Gemälde der Familie Blackwood.",
        "image": "foyer.jpg"
    },
    {
        "name": "Bibliothek",
        "x": 3,
        "y": 0,
        "width": 3,
        "height": 3,
        "color": "lightblue",
        "description": "Eine ruhige Bibliothek mit hohen Bücherregalen, vollgestopft mit alten Büchern. In der Mitte steht ein massiver Eichentisch, auf dem noch geöffnete Bücher und ein Glas Whiskey stehen.",
        "image": "bibliothek.jpg"
    }
] 

Part of my program:

#[derive(Debug, serde::Deserialize, Asset, TypePath)]
struct Room {
    name: String,
    x: i32,
    y: i32,
    width: u32,
    height: u32,
    color: String,
    description: String,
    image: String,
}
#[derive(serde::Deserialize, Asset, TypePath)]
struct Rooms {
    rooms: Vec<Room>,
}

#[derive(Resource)]
struct RoomsHandle(Handle<Rooms>);

fn main() {
    App::new()
        .add_plugins((
            DefaultPlugins,
            JsonAssetPlugin::<Rooms>::new(&["rooms.json"]),
        ))
        .insert_resource(Msaa::Off)
        .init_state::<AppState>()
        .add_systems(Startup, setup)
        .add_systems(Update, spawn_plane.run_if(in_state(AppState::Loading)))
        .run();
}

fn setup(
    mut commands: Commands,
    asset_server: Res<AssetServer>
) {
    let rooms = RoomsHandle(asset_server.load("cases/Mord im Herrenhaus/data/rooms.json"));
    commands.insert_resource(rooms);
...
}

fn spawn_plane(
    mut commands: Commands,
    room: Res<RoomsHandle>,
    mut meshes: ResMut<Assets<Mesh>>,
    mut materials: ResMut<Assets<StandardMaterial>>,
    mut rooms: ResMut<Assets<Rooms>>,
    mut state: ResMut<NextState<AppState>>,
) {

    if let Some(room) = rooms.remove(room.0.id()) {
        for r in room.rooms  {
            println!("{:?}",r);
        }

        state.set(AppState::Running);
    }
}

There I get the message " Failed to load asset 'cases/Mord im Herrenhaus/data/rooms.json' with asset loader 'bevy_common_assets::json::JsonAssetLoader': Could not parse the JSON: invalid type: map, expected a sequence at line 2 column 4 "

Whats wrong?

Best regards Gerhard

cactusdualcore commented 3 months ago

@gkvoelkl I believe this behaves as intended.

#[derive(serde::Deserialize, Asset, TypePath)]
struct Rooms {
    rooms: Vec<Room>,
}

What should the above struct serialize to as json? I'd say roughly the following:

{
  "rooms": [
    // the 'Room' objects here.
  ]
}

I am not certain about this, but I assume serde is fine with not specifying the Rooms object as a literal JSON object, because it only has one field. So it allows for swapping the outermost object with an array, like this:

[
  [
    // the 'Room' objects here.
  ]
]

Try putting the #[serde(transparent)] attribute on the Rooms struct. I think this could fix your issue.

EDIT: I haven't tested this at all, just a guess

NiklasEi commented 3 months ago

Yeah, your .json file is missing the rooms field in a json object. It should work with the first example json from above.

Also, Room is not an asset (at least not for the code you shared). It should be fine to remove the Asset and TypePath derives for that type.