0

I have a machine-generated class MyData which has sbyte members. The actual class is long and not very readable, but here is a fragment of it:

class MyData
{
    private sbyte _MuxControl;
    public sbyte MuxControl 
    { 
        get { return _MuxControl; } 
        set { __isset.MuxControl = true; this._MuxControl = value; }
    }
}

The corresponding simplified JSON looks like this:

[ 
  { 
    "MuxControl": 0xAA
  }
]

I am attempting to deserialize like this:

var deserialized = JsonConvert.DeserializeObject<List<MyData>>(JsonStr);

Some values exceed sbyte range, for example 0xAA. As a result, exceptions are thrown. When I change the value to 0x1, for example, it works.

I can not touch the code of MyData. It's machine-generated. Is there a conversion setting, override or some other way to get these values to deserialize properly into sbyte?

Brian Rogers
  • 125,747
  • 31
  • 299
  • 300
user1439579
  • 131
  • 10
  • Can you share the JSON you are trying to deserialize, the definition of `MyData` and the desired treatment when one of the values is out of range? – Brian Rogers Jul 25 '19 at 17:39
  • @BrianRogers Sure. As I wrote, `MyData` is machine-generated, so, long and unreadable. I can share fragment of it: `private sbyte _MuxControl;` `public sbyte MuxControl { get { return _MuxControl; } set { __isset.MuxControl = true; this._MuxControl = value; } }` Json has something `"MuxControl": 0xAA,` – user1439579 Jul 25 '19 at 18:44

1 Answers1

1

You are getting the exception because the conversion methods Json.Net is using under the covers for sbyte are range checked, but what you really need here is an unchecked conversion (or more precisely, a bigger range). You can handle that with a custom JsonConverter like so:

public class SByteConverter : JsonConverter
{
    public override bool CanConvert(Type objectType)
    {
        return objectType == typeof(sbyte);
    }

    public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
    {
        if (reader.TokenType == JsonToken.Integer)
        {
            // Integer values come in as longs from the reader.
            long val = (long)reader.Value;

            // If the value fits in 8 bits, convert it to a signed byte.
            if (val >= -128 && val <= 255)
            {
                return unchecked((sbyte)val);
            }

            // We got a value that can't fit in an sbyte.
            throw new JsonSerializationException("Value was out of range for an sbyte: " + val);
        }

        // We got something we didn't expect, like a string or object.
        throw new JsonSerializationException("Unexpected token type: " + reader.TokenType);
    }

    public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
    {
        // Write sbyte values out in the same format we read them.
        // Note this is technically invalid JSON per the spec.
        writer.WriteRawValue("0x" + ((sbyte)value).ToString("X2"));
    }
}

To use the converter, pass an instance of it to JsonConvert.DeserializeObject like this:

var deserialized = JsonConvert.DeserializeObject<List<MyData>>(JsonStr, new SByteConverter());

Working demo: https://dotnetfiddle.net/fEW6wy

Brian Rogers
  • 125,747
  • 31
  • 299
  • 300
  • Shouldn't it be +127 for sbyte (Or sbyte.MinValue/sbyte.MaxValue) ? – knechtrootrecht May 06 '20 at 12:04
  • 1
    @ivo Normally, yes. But the whole problem we are trying to solve here is to allow values that are *above* 127, but still fit in 8 bits, such as 170 (0xAA) to be cast to an `sbyte` per the OP's requirements. The `unchecked` keyword allows us to do that without throwing an exception. I opted to leave the lower end of the range as -128 instead of 0 just in case there happened to be `sbyte` values in the data other than `MuxControl` which were represented as negative integers. I wanted to allow those to still convert normally. – Brian Rogers May 06 '20 at 15:29
  • Ah, yes. now it makes sense. Thank you very much for clarifying this – knechtrootrecht May 11 '20 at 05:26