Json.Net/SupportedDisplayModes Issue

Hi,

I installed NewtonSoft’s Json.Net. It includes a simple converter:

string output = JsonConvert.SerializeObject(x);

This works fine on project A (non-monogame).
But it crashes on project B (which uses monogame) with the error:

Error getting value from ‘SupportedDisplayModes’ on ‘Microsoft.Xna.Framework.Graphics.GraphicsAdapter’.

Does anyone have any suggestions?

Please post the actual code you’re trying to run.

What exactly are you trying to serialize?

You shouldn’t be trying to serialize the GraphicsAdaptor object. There is no need or purpose in doing so.

I just meant x to refer to “some object of a class I made”.

I was trying to apply the simple JsonConvert example code provided at
https://www.newtonsoft.com/json/help/html/SerializingJSON.htm#JsonConvert
which is basically:

Product product = new Product();
product.Name = “Apple”;
string output = JsonConvert.SerializeObject(product);

However, I was applying this example to a class in my game which has both static and non-static texture2D fields. I think that was the error.

I presume you’re doing this for some sort of config system right?

If so… what I would do, is if the config file doesn’t already exist, set it up with the default values you want, but when it comes to stuff related to display mode, make sure that the default values are what the user has set in their system.

You can get that information with the GraphicsAdapter.DefaultAdapter.CurrentDisplayMode property.

Be sure to create a CUSTOM C# object to store the relevant information. You shouldn’t directly serialize a DisplayMode. For example, if you’re just storing resolution:

public class MyConfigObject
{
  public int Width { get; set; }
  public int Height { get; set; }
}

That’ll be what you (de)serialize.

So, to create the config file:

//Create a default config object to store resolution settings
var config = new MyConfigObject
{
  Width = GraphicsAdapter.DefaultAdapter.CurrentDisplayMode.Width,
  Height = GraphicsAdapter.DefaultAdapter.CurrentDisplayMode.Height
};
//Serialize to JSON
string json = JsonConvert.SerializeObject(config);
//Do whatever with that string to save to disk or wherever you plan to store it.

Then, when loading it in:

//Deserialize the config JSON
var config = JsonConvert.DeserializeObject<MyConfigObject>(json);
//Retrieve resolution info
int w = config.Width;
int h = Config.Height;
//Do whatever you plan to do with said info
//For example...set the game's preferred backbuffer size.
//You'd do this somewhere in Game1.cs.
graphicsDevice.PreferredBackBufferWidth = w;
graphicsDevice.PreferredBackBufferHeight = h;
graphicsDevice.ApplyChanges();

But there is a flaw with that code. It doesn’t check that the display mode in the config file is actually supported by the system. So, I could for example go into the config and set the width/height to some outrageously big numbers and break your game.

To fix that, right after retrieving the config values for the display mode, check them to see if they match anything in GraphicsAdapter.DefaultAdapter.SupportedDisplayModes. You can use LINQ for that. If you don’t get any matching display mode, reset the config values for the display mode to the system default (like you did when you created the file), then save to disk and apply.

For example:

//Right after retrieving config values and before applying, check if the display mode is supported.
var supportedModeMatch = GraphicsAdapter.DefaultAdapter.SupportedDisplayModes.FirstOrDefault(x=>x.Width == width && x.Height == height);
//That LINQ query will search the list for the first element where that lambda expression evaluates to true. AKA: the first display mode with a Width and Height matching the config values.
//
//If it doesn't find any, it will return null.

if(supportedModeMatch == null)
{
  //Reset config values, save, then apply them.
}
else
{
  //Just apply 'em! You're good to go.
}

That should help ya. No need to serialize GraphicsAdapter directly.

Gulp. You’re making me feel guilty Watercolor!
An interesting read though!

Sorry if I made ya feel guilty… :stuck_out_tongue:

Just thought I’d share how I use JSON.NET in my game’s engine since I use it quite a lot actually. I use it for configuration files, communication between the client and the server (I’m over-simplifying, but I wrote my own server-side protocol that allows message bodies to be expressed in JSON), and I used to use it as a way to store game world information (I do a lot of procedural generation for my games, world data is never baked in to the game.)

It’s very useful. Though personally, although this is unrelated to the topic, when it comes to things I DON’T want the user to be muckin’ about in, I use Whoa to serialize objects to files. It’s not a well-known serializer - it’s made by a friend of mine who also develops my game with me. It’s a binary serializer that trades off the ability to easily read/edit in a text editor for a smaller file size. It’s actually on NuGet and is SIMILAR to JSON.NET in how you use it.

Also unrelated, if you’re ever doing server-side stuff that involves user accounts and stuff like that, LiteDB might also be a pretty big help. It is an open-source, completely free, server-less database system similar to MongoDB written completely in .NET. I haven’t tried it yet but sometime in the next few days I plan to migrate my game’s server over to it (and also plan to use it for save files in Singleplayer.)

So if I’m understanding you right the class looks something like this:

public class MyThing
{    
    public Texture2D Texture { get; set; }
}

And you’re trying to do something like this:

var x = new MyThing();
x.Texture = Content.Load<Texture2D>("my-texture");
var output = JsonConvert.SerializeObject(x);

The problem with this approach is that JSON.NET walks all of the properties of the object and tries to serialize everything in the object. When it gets to the Texture property it starts walking into it and finds the GraphicsDevice property and tries to serialize that too. Then it finds Adapter and so on until it gets to SupportedDisplayModes and gets the error.

The exact reason for the error is complicated. But it’s safe to say this is almost certainly not what you intended when you tried to serialize the object in the first place. More than likely, what you expected in the JSON output was something more like this:

{
   "Texture": "my-texture"
}

As it turns out, getting this to work is actually not really that difficult once you understand how to write a simple custom JsonConverter.

Something like this should work (wrote this off the top of my head, I think it works).

    public class Texture2DJsonConverter : JsonConverter
    {
        private readonly ContentManager _contentManager;

        public Texture2DJsonConverter(ContentManager contentManager)
        {
            _contentManager = contentManager;
        }

        public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
        {
            var texture = (Texture2D)value;
            writer.WriteValue(texture.Name);
        }

        public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
        {
            var textureName = reader.Value as string;
            return textureName == null ? null : _contentManager.Load<Texture2D>(textureName);
        }

        public override bool CanConvert(Type objectType)
        {
            return objectType == typeof(Texture2D);
        }
    }

Then all you have to do is pass your converter into the serializer.

var conveter = new Texture2DConverter(Content);
var output = JsonConvert.SerializeObject(x, converter);

Of course, once you understand how to do this you can write converters for all sorts of things and you’ll be serializing just about anything you can imagine.

We have a whole bunch of these in MonoGame.Extended you might want to borrow. Feel free to take what you like.

1 Like

Alternatively you can have a string property that has the value of the path to the texture and explicitly ignore the texture property itself when serializing by adding an attribute (I don’t know which one by heart, sorry). If you meant to serialize the actual contents of the texture (as in the color data), things are slightly more complicated, but feel free to ask if that’s the case :slight_smile:

There’s NonSerialized for fields, XmlIgnore for both fields and properties. DataContract serializers use those and their own set. It gets nasty fast. I can’t keep track of them either.

Slightly better approach is to wrap the resource in something that does all the above but takes care of how to get the resource for reconstruction/load. Although you can do this inside a setter you’d have to do it all over the place.

Json’s lack of a solid alternative for XPath queries really hurts when it comes to dependencies and serialization.

1 Like

Don’t forget about JsonIgnore ;). I’ve never been a big fan of the ignored property paired with the “only for serialization” property. It muddies up the model and adds unneeded complexity. Not to mention it’s a leaky abstraction.

The wrapper approach feels a lot like the decorator pattern. This can work okay if you’ve only got a handful of classes to wrap, but it can get pretty tiresome if you’re wrapping lots of classes, especially if most of the properties are already serializable.

For example, let’s say you had a class with 20 properties and one of those is a Texture2D. You write a wrapper class and you have to re-implement all 20 properties doing something special for just one of them. Okay, fine. Then later, you have another class you need to serialize, it also happens to have a Texture2D property. Ugh, so you have to write another wrapper for this one for the same reason.

With the JsonConverter approach, you only have to write the converter once. Done. Works for all classes that have Texture2D properties so long as you remember to pass it into the serializer. Alternately, you can apply the converter as an attribute on the properties, or setup a converter contract resolver. Lots of options.

What about JSON.NET’s SelectToken? I can see how this might be useful at times. Personally I’ve been never it though.

In my experience, JSON.NET is an incredibly well thought out library. The documentation is excellent and with a little reading you’ll probably find a pretty good solution to most problems.