0

I am trying to use a json converter without success.

I have a sensor class which can (de)serialize json:

internal class JsonSensor<T> : IJsonSensor where T : struct
{
    [JsonProperty(PropertyName = "value")]
    public T Value { get; set; }

    [JsonProperty(PropertyName = "state")]
    public SensorState State { get; set; } = SensorState.Unknown;

    public override string ToString()
    {
        return $"Value={Value}; State={State}";
    }
}

Then I have a long list of sensors, I'll focus on the first one so ignore the rest for sake of simplicity:

[Function(Name = "sensors")]
internal class Sensors
{
    [CabinetDoubleSensor(DisplayName=CabinetSensorNames.RcwPcwSupPrs, SensorType =  SensorType.PressureInBar, Precision=2)]
    [JsonProperty(PropertyName = "IO_PCW_FL_SPR")]
    [JsonConverter(typeof(BarToPascalConverter))]
    public JsonSensor<double> IOPcwFlSpr { get; set; } = new JsonSensor<double>();

    [CabinetDoubleSensor(DisplayName=CabinetSensorNames.RcwPcwRetPrs, SensorType =  SensorType.PressureInBar, Precision=2)]
    [JsonProperty(PropertyName = "IO_PCW_RL_SPR")]
    [JsonConverter(typeof(BarToPascalConverter))]
    public JsonSensor<double> IOPcwRlSpr { get; set; } = new JsonSensor<double>();

    ...
    ...
    ...
}

And, I have converters that convert the value from one metric system to another. E.g. the BarToPascal converter:

internal class BarToPascalConverter : FactorConverterBase
{
    public BarToPascalConverter() : base(1e5)
    {
    }
}

internal abstract class FactorConverterBase : JsonConverter<JsonSensor<double>>
{
    public double Factor { get; protected set; }

    protected FactorConverterBase(double factor)
    {
        Factor = factor;
    }

    public override void WriteJson(JsonWriter writer, JsonSensor<double> value, JsonSerializer serializer)
    {
        writer.WriteValue(value);
    }

    public override JsonSensor<double> ReadJson(JsonReader reader, Type objectType, JsonSensor<double> existingValue, bool hasExistingValue,
        JsonSerializer serializer)
    {
        var o = reader.Value;
        //existingValue.Value = ((JsonSensor<double>)reader.Value).Value / Factor;
        return new JsonSensor<double>();
    }
}

The problem is in the ReadJson method. There I am doing something wrong which leads to the following exception:

Newtonsoft.Json.JsonSerializationException : Additional text found in JSON string after finishing deserializing object. Path '', line 1, position 45.

I wrote a unit test to isolate the problem, and try different implementations for the ReadJson method, but I found that even when I 'just return' a new JsonSensor<double>() deserializing still fails.

Of course, when I remove the JsonConverter, I can deserialize the string. So that tells me :

  • the supplied json string is OK.
  • the class structure for json deserialization is OK.

What am I doing wrong in the ReadJson? How can I successfully read the JsonSensor<double> from the reader, and convert its value?

For completeness the unit test:

[TestFixture]
public class GetSensorsTests
{
    [Test]
    public void GetSensors()
    {
        const string test = "{\"IO_PCW_FL_SPR\" : {\"value\":123.0,\"state\":1}}";

        var sensors = JsonConvert.DeserializeObject<Sensors>(test);
        Assert.AreEqual(123.0, sensors.IOPcwFlSpr.Value, 0.00001);
    }
}
bas
  • 13,550
  • 20
  • 69
  • 146

1 Answers1

0

This seems to work, not sure if this is the intended way how this should work.

    public override JsonSensor<double> ReadJson(JsonReader reader, Type objectType, JsonSensor<double> existingValue, bool hasExistingValue,
        JsonSerializer serializer)
    {
        var jsonObject=JObject.Load(reader);
        var s = JsonConvert.DeserializeObject<JsonSensor<double>>(jsonObject.ToString());
        s.Value = s.Value / Factor;
        return s;
    }

Update after dbc's comment:

    public override void WriteJson(JsonWriter writer, JsonSensor<double> value, JsonSerializer serializer)
    {
        value.Value = value.Value * Factor;
        var json = JsonConvert.SerializeObject(value);
        writer.WriteValue(json);
    }

    public override JsonSensor<double> ReadJson(JsonReader reader, Type objectType, JsonSensor<double> existingValue, bool hasExistingValue,
        JsonSerializer serializer)
    {
        var jsonSensor = JObject.Load(reader).ToObject<JsonSensor<double>>();
        jsonSensor.Value = jsonSensor.Value / Factor;
        return jsonSensor;
    }
bas
  • 13,550
  • 20
  • 69
  • 146
  • Yes. The reader is positioned at the start of an object, and you're not consuming that object in your custom converter. I suspect you could simplify your example very significantly (no need for a base converter class, no need for generics) and still demonstrate the same problem, by the way. – Jon Skeet Sep 16 '19 at 07:45
  • `jsonObject.ToObject>()` will be more efficient as you thereby avoid the conversion to string and re-parse. – dbc Sep 16 '19 at 07:54
  • @dbc that a good remark, thanks. Is there also something fancier to do for the `WriteJson` ? Or is that fine as it is since it's going to be converted to text anyway? – bas Sep 16 '19 at 08:25
  • @JonSkeet Yeah sorry will put in effort to get rid of irrelevant parts next time. – bas Sep 16 '19 at 08:26
  • I think you may be double-serializing your string in `WriteJson()`. You may need to use `WriteRawValue()` instead of `WriteValue()`. – dbc Sep 16 '19 at 08:28
  • Sorry missed that last comment, it works like a charm this way, so I think i'm good. Thx for the help!! – bas Sep 18 '19 at 13:52