1
votes

I'm creating an RPG game and I need to load maps with Tiled. I have the Tiled part down (I'm using MonoGame.Extended). But I need extra data about the map. My plan is to use a JSON file that holds the necessary information. However, I would like to have it through the content pipeline as it is directly related to the tile maps.

I've attempted to use a custom content pipeline extension. It used JSON.Net to deserialize the JSON file into a Dictionary<string, dynamic>. However, when I compiled the DLL file and tried to reference it into the pipeline tool the pipeline tool would always crash.

Content Importer:

using System;
using System.Collections.Generic;
using System.IO;

using Microsoft.Xna.Framework.Content.Pipeline;

using Newtonsoft.Json;

namespace JsonExtension
{

    [ContentImporter(".json", DefaultProcessor = "JsonProcessor")]
    public class JsonImporter : ContentImporter<Dictionary<string, dynamic>>
    {

        public override Dictionary<string, dynamic> Import(string filename, ContentImporterContext context)
        {
            context.Logger.LogMessage("Importing JSON file: {0}", filename);

            using (var streamReader = new StreamReader(filename))
            {
                JsonSerializer serializer = new JsonSerializer();
                return (Dictionary<string, dynamic>)serializer.Deserialize(streamReader, typeof(Dictionary<string, dynamic>));
            }
        }

    }

}

Content Processor:

using System;
using System.Collections.Generic;

using Microsoft.Xna.Framework.Content.Pipeline;

namespace JsonExtension
{
    [ContentProcessor]
    public class JsonProcessor : ContentProcessor<Dictionary<string, dynamic>, Dictionary<string, dynamic>>
    {
        public override Dictionary<string, dynamic> Process(Dictionary<string, dynamic> input, ContentProcessorContext context)
        {
            context.Logger.LogMessage("Processing JSON");

            return input;
        }
    }
}

Content Type Writer:

using System;
using System.Collections.Generic;
using System.IO;

using Microsoft.Xna.Framework.Content.Pipeline;
using Microsoft.Xna.Framework.Content.Pipeline.Serialization.Compiler;

using Newtonsoft.Json;

namespace JsonExtension
{
    [ContentTypeWriter]
    class JsonTypeWriter : ContentTypeWriter<Dictionary<string, dynamic>>
    {
        protected override void Write(ContentWriter output, Dictionary<string, dynamic> value)
        {
            output.Write(JsonConvert.SerializeObject(value));
        }

        public override string GetRuntimeType(TargetPlatform targetPlatform)
        {
            return typeof(Dictionary<string, dynamic>).AssemblyQualifiedName;
        }

        public override string GetRuntimeReader(TargetPlatform targetPlatform)
        {
            return "JsonExtension.JsonReader";
        }
    }
}

Is there a better way to store Tiled map meta-data? Or is there a better way to import it through the pipeline? Or am I doing something wrong with my content importer?

1
What is the error message associated with the crash? - Strom
You can add custom properties in Tiled to the map, to the layers and even to each object placed in Tiled. I use TiledSharp instead of Monogame.Extended but I am sure it also supports the usage of custom props. - Pavel Slesinger
System.Reflection.ReflectionTypeLoadException: Unable to load one or more of the requested types. Retrieve the LoaderExceptions property for more information That is the error message from the content pipeline - minebolt

1 Answers

1
votes

The error is due to the dynamic type. The pipeline requires all types to be resolvable and instantiatable through Reflection.

There is a secondary resolver for Generic collections to Serialize and Deserialize each type in the Generic. The line Dictionary<string, dynamic> has two types and and unknowable type associated System.Collections.Generic.Dictionary, System.String and dynamic. The dynamic has no hints as to what type to use and cannot be resolved resulting in the error.

The simple solution is to store the data as a json String(which you are already doing now except the type you are attempting to store it in is dynamic) and Deserialize the String into its object after the Content.Load has returned the data.

In other words use Dictionary<string, string> as the content processor type.


An easier solution would be to copy the JSON file to the output directory. Build action "copy if newer" and use System.IO.File to read it and JSON.NET to deserialize it into an object in the Initialize method of Game1.