Search code examples
c#azure-iot-hubazure-iot-sdk

Azure IOT Hub SDK set device twin desired property with a boolean value not working


I'm having an issue setting some desired properties with a boolean value when compiling the json string, I get an exception back from the azure Hub SDK with ""Unexpected character encountered while parsing value: T. Path 'properties.desired.smtpServer.logEvents', line 9, position 39.""

             var twin = await _registryManager.GetTwinAsync(deviceId);

             var patch =           
             @"{
                properties: {
                    desired: {
                        smtpServer: {
                            encoding: '" + server.Encoding + @"',
                            dataLimit: " + server.DataLimit + @",
                            ipAddress: '" + server.IpAddress + @"',
                            portNumber: " + server.PortNumber + @",
                            logEvents: " + true + @", // not working - doesnt like the boolean
                            autoStart: " + true + @", // not working - doesnt like the boolean
                        }
                    }
                }
            }";

            await _registryManager.UpdateTwinAsync(twin.DeviceId, patch, twin.ETag);

If is set the property logEvents to a string then it goes through OK.

Looking at other desired property examples, there is no reason why we cant use booleans rather than converting them to strings, is there something wrong with the way I'm building up the json? Completely lost what the issue is here...

Update - A different approach but still not working:

Going by an answer given below, I then tried using a Newtonsoft Serializer but now find that the desired properties are not updating at all, but equally I'm not getting any exceptions back from the IOT Hub SDK so I'm not sure how to troublshoot this.

Model Classes:

public class properties
{
    [JsonProperty("desired")]
    public desired desired { get; set; }
}

public class desired
{
    [JsonProperty("smtpServer")]
    public smtpServer smtpServer { get; set; }
}

public class smtpServer
{
    [JsonProperty("encoding")]
    public string encoding { get; set; }

    [JsonProperty("dataLimit")]
    public int dataLimit { get; set; }

    [JsonProperty("ipAddress")]
    public string ipAddress { get; set; }

    [JsonProperty("portNumber")]
    public int portNumber { get; set; }

    [JsonProperty("logEvents")]
    public bool logEvents { get; set; }

    [JsonProperty("autoStart")]
    public bool autoStart { get; set; }

    [JsonProperty("enabled")]
    public bool enabled { get; set; }
}

Update method:

    public async Task UpdateServer(string deviceId, smtpServer server)
    {
        var twin = await _registryManager.GetTwinAsync(deviceId);

        var smtpServer = new smtpServer
        {
            encoding = server.encoding,
            dataLimit = server.dataLimit,
            ipAddress = server.ipAddress,
            portNumber = server.portNumber,
            logEvents = server.logEvents,
            autoStart = server.autoStart,
            enabled = server.enabled,
        };

        var desired = new desired
        {
            smtpServer = smtpServer
        };

        var properties = new properties
        {
            desired = desired
        };

        //var patch = JsonConvert.SerializeObject(properties, Formatting.Indented);
        var patch = JsonConvert.SerializeObject(properties);
        await _registryManager.UpdateTwinAsync(twin.DeviceId, patch, twin.ETag);
    }

Solution

  • IoT Hub will not accept the JSON in this form, because using the value true and adding it to a string will convert it to True (notice the upper case T). This is not a valid value for the JSON parser. Make sure the value you send is lower case.

    While that will fix the exception, and fix your problem (IoT Hub will accept this text just fine), this is still not valid JSON. All the properties and string values should be surrounded by double-quotes. You want your JSON to end up looking like this:

    {
       "properties":{
          "desired":{
             "smtpServer":{
                "encoding":"yes",
                "dataLimit":100,
                "ipAddress":"yes",
                "portNumber":22,
                "logEvents":true,
                "autoStart":true
             }
          }
       }
    }
    

    It gets quite tedious to write the JSON out yourself, so you're better off using a JSON serializer instead. The Azure SDK uses Newtonsoft.Json internally, so you could do the following and not call a serializer yourself:

    private static async Task UpdateTwin()
    {
        var smtpServer = new SmtpServer
        {
            Encoding = "bar",
            DataLimit = 100,
            IpAddress = "foo",
            PortNumber = 42,
            LogEvents = true,
            AutoStart = true
        };
        var registryManager = RegistryManager.CreateFromConnectionString(ConnectionString);
        var twin = await registryManager.GetTwinAsync(TestDevice);
        twin.Properties.Desired["smtpServer"] = smtpServer;
        await registryManager.UpdateTwinAsync(twin.DeviceId, twin, twin.ETag);
    }
    
    public class SmtpServer
    {
        [JsonProperty("encoding")] 
        public string Encoding { get; set; }
    
        [JsonProperty("dataLimit")] 
        public int DataLimit { get; set; }
    
        [JsonProperty("ipAddress")] 
        public string IpAddress { get; set; }
    
        [JsonProperty("portNumber")] 
        public int PortNumber { get; set; }
    
        [JsonProperty("logEvents")] 
        public bool LogEvents { get; set; }
        
        [JsonProperty("autoStart")] 
        public bool AutoStart { get; set; }
    }
    

    Tested that code on my hub and it worked without issue.