0
\$\begingroup\$

I am trying to build a JSON tile map importer for the content pipeline of Monogame but I'm having difficulty doing so. I have been following this tutorial and got something... But now it appears my entire JSON level cannot be loaded since it has an array-based structure, and my code expects an object, here is my level file:

[
    [1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
    [2,0,0,0,0,0,0,0,2,2,2,0,0,0,0,0,0,0,0,0,1,1],
    [2,1,0,0,0,0,0,0,0,0,0,0,1,1,1,0,0,0,1,1,2,2],
    [2,2,1,1,1,0,0,0,0,1,1,1,2,2,2,1,0,0,0,0,2,2],
    [2,2,0,0,0,0,0,0,1,2,2,2,2,2,2,2,1,0,0,0,2,2],
    [2,0,0,0,0,0,1,1,2,2,2,2,2,2,2,2,2,1,1,1,2,2],
    [2,0,0,0,1,1,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2],
    [2,1,1,1,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2],
    [2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2],
    [2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2]
]

And below are the custom content pipeline classes I made:

public class MapFile
{
    private MapFile map;

    public MapFile(MapFile map)
    {
        this.map = map;
    }

    public int[,] MapContent { get; set; }
}

[ContentImporter(".json", DefaultProcessor = "TileMapProcessor",
DisplayName = "TileMap Importer")]
public class MapImporter : ContentImporter<MapFile>
{
    public override MapFile Import(string filename, ContentImporterContext context)
    {
        context.Logger.LogMessage("Importing Map file: {0}", filename);

        using (StreamReader streamReader = new StreamReader(filename))
        {
            JsonSerializer serializer = new JsonSerializer();
            return (MapFile)serializer.Deserialize(streamReader, typeof(MapFile));
        }
    }
}

public class MapReader : ContentTypeReader<MapFile>
{
    protected override MapFile Read(ContentReader input, MapFile existingInstance)
    {
        string json = input.ReadString();
        StreamReader reader = new StreamReader(json);
        JsonSerializer serializer = new JsonSerializer();
        MapFile mapFile = (MapFile)serializer.Deserialize(reader, typeof(MapFile));
        MapFile map = input.ContentManager.Load<MapFile>(mapFile.MapContent.ToString());
        return new MapFile(map);
    }
}

The problem: As I said above, I get the following error at the content pipeline app:

Newtonsoft.Json.JsonSerializationException: Cannot deserialize the current > JSON array (e.g. [1,2,3]) into type 'my class' because the type requires a > JSON object (e.g. {"name":"value"}) to deserialize correctly.

To fix this error either change the JSON to a JSON object (e.g. >{"name":"value"}) or change the deserialized type to an array or a type >that implements a collection interface (e.g. ICollection, IList) like List > that can be deserialized from a JSON array. JsonArrayAttribute can also be > added to the type to force it to deserialize from a JSON array.

Changing the type from int[,] to List<List<int>> would be easy, but I have the following code to generate visual representation of the map files:

public void Generate(MapFile map, int size)
{
    for (int x = 0; x < map.MapContent.GetLength(1); x++)
    {
        for (int y = 0; y < map.MapContent.GetLength(0); y++)
        {
            int number = map.MapContent[y, x];
            if (number > 0)
            {
                CollisionTiles.Add(new CollisionTiles(number, new Rectangle(x * size, y * size, size, size)));
            }
            Width = (x + 1) * size;
            Height = (y + 1) * size;
        }
    }
}

So that is what I am using. I hope you can help me, I've got everything right, including the collision so loading my maps from XNB files is the last thing I need to do. Thanks in advance! Edit: new level format:

{
    "Name": "My Level",
    "MapContent": 
    [
      1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
      2,0,0,0,0,0,0,0,2,2,2,0,0,0,0,0,0,0,0,0,1,1,
      2,1,0,0,0,0,0,0,0,0,0,0,1,1,1,0,0,0,1,1,2,2,
      2,2,1,1,1,0,0,0,0,1,1,1,2,2,2,1,0,0,0,0,2,2,
      2,2,0,0,0,0,0,0,1,2,2,2,2,2,2,2,1,0,0,0,2,2,
      2,0,0,0,0,0,1,1,2,2,2,2,2,2,2,2,2,1,1,1,2,2,
      2,0,0,0,1,1,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,
      2,1,1,1,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,
      2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,
      2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2
    ]
}
\$\endgroup\$
1
\$\begingroup\$

I think your level file needs to look like this:

{
    "MapContent": 
    [
    [1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
    [2,0,0,0,0,0,0,0,2,2,2,0,0,0,0,0,0,0,0,0,1,1],
    [2,1,0,0,0,0,0,0,0,0,0,0,1,1,1,0,0,0,1,1,2,2],
    [2,2,1,1,1,0,0,0,0,1,1,1,2,2,2,1,0,0,0,0,2,2],
    [2,2,0,0,0,0,0,0,1,2,2,2,2,2,2,2,1,0,0,0,2,2],
    [2,0,0,0,0,0,1,1,2,2,2,2,2,2,2,2,2,1,1,1,2,2],
    [2,0,0,0,1,1,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2],
    [2,1,1,1,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2],
    [2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2],
    [2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2]
    ]
}

Note that I've changed the outer square brackets ([, ]) to curly braces ({, }) because you're deserializing into a JSON object.

It may look a little odd that you have to add the MapContent property to the file but one advantage of this approach is that you'll be able to easily add extra metadata to your map file. For example, you could add a Name property like this:

{
    "Name": "My Level",
    "MapContent": 
    [
    [1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
    [2,0,0,0,0,0,0,0,2,2,2,0,0,0,0,0,0,0,0,0,1,1],
    [2,1,0,0,0,0,0,0,0,0,0,0,1,1,1,0,0,0,1,1,2,2],
    [2,2,1,1,1,0,0,0,0,1,1,1,2,2,2,1,0,0,0,0,2,2],
    [2,2,0,0,0,0,0,0,1,2,2,2,2,2,2,2,1,0,0,0,2,2],
    [2,0,0,0,0,0,1,1,2,2,2,2,2,2,2,2,2,1,1,1,2,2],
    [2,0,0,0,1,1,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2],
    [2,1,1,1,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2],
    [2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2],
    [2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2]
    ]
}

And change your MapFile class accordingly

public class MapFile
{
    public string Name { get; set; }
    public int[,] MapContent { get; set; }
}

Btw, while we are on the topic. I find it very strange that you are passing a MapFile into the MapFile constructor. I think you can remove the constructor completely to simplify the code.

public MapFile(MapFile map)

If you really don't want to change the level format you might be able to change the deserialization method instead. However, I think this approach is going to lead to some pretty confusing code.

\$\endgroup\$
  • \$\begingroup\$ I got the following error: We don't support multidimensional arrays!. It doesn't say anything about Newtonsoft.Json so my guess would be the problem is in the Content pipeline itself? I'll fire an issue on the official repo. \$\endgroup\$ – PowerUser Jun 7 '16 at 12:08

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.