diff --git a/Loxone.Client.Tests/DeserializationTests.cs b/Loxone.Client.Tests/DeserializationTests.cs
new file mode 100644
index 0000000..141f8a2
--- /dev/null
+++ b/Loxone.Client.Tests/DeserializationTests.cs
@@ -0,0 +1,43 @@
+// ----------------------------------------------------------------------
+//
+// Copyright (c) The Loxone.NET Authors. All rights reserved.
+//
+//
+// Use of this source code is governed by the MIT license that can be
+// found in the LICENSE.txt file.
+//
+// ----------------------------------------------------------------------
+
+namespace Loxone.Client.Tests
+{
+ using System;
+ using Loxone.Client.Transport;
+ using Loxone.Client.Transport.Serialization.Responses;
+ using Microsoft.VisualStudio.TestTools.UnitTesting;
+
+ [TestClass]
+ public class DeserializationTests
+ {
+ [TestMethod]
+ public void DeserializeResponseNumberCodeLowercase()
+ {
+ string s = @"{""LL"":{""control"":""jdev/sys/getkey2/user"",""code"":200,""value"":{""key"":""0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF"",""salt"":""0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF012345""}}}";
+ var r = LXResponse.Deserialize(s);
+ Assert.AreEqual(200, r.Code);
+ Assert.AreEqual("jdev/sys/getkey2/user", r.Control);
+ Assert.IsNotNull(r.Value);
+ }
+
+ [TestMethod]
+ public void DeserializeResponseStringCodeUppercase()
+ {
+ string s = @"{""LL"": { ""control"": ""dev/cfg/api"", ""value"": ""{'snr': 'AA:BB:CC:DD:EE:FF', 'version':'10.3.4.10'}"", ""Code"": ""200""}}";
+ var r = LXResponse.Deserialize(s);
+ Assert.AreEqual(200, r.Code);
+ Assert.AreEqual("dev/cfg/api", r.Control);
+ Assert.IsNotNull(r.Value);
+ Assert.AreEqual(SerialNumber.Parse("AA:BB:CC:DD:EE:FF"), r.Value.SerialNumber);
+ Assert.AreEqual(new Version(10, 3, 4, 10), r.Value.Version);
+ }
+ }
+}
diff --git a/Loxone.Client.Tests/Loxone.Client.Tests.csproj b/Loxone.Client.Tests/Loxone.Client.Tests.csproj
new file mode 100644
index 0000000..1fd7ccc
--- /dev/null
+++ b/Loxone.Client.Tests/Loxone.Client.Tests.csproj
@@ -0,0 +1,14 @@
+
+
+ netcoreapp3.1
+ false
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/Loxone.Client/Loxone.Client.csproj b/Loxone.Client/Loxone.Client.csproj
index 11e2ee4..646b82d 100644
--- a/Loxone.Client/Loxone.Client.csproj
+++ b/Loxone.Client/Loxone.Client.csproj
@@ -9,7 +9,7 @@
manison
-
+
diff --git a/Loxone.Client/Properties/AssemblyInfo.cs b/Loxone.Client/Properties/AssemblyInfo.cs
new file mode 100644
index 0000000..5414a17
--- /dev/null
+++ b/Loxone.Client/Properties/AssemblyInfo.cs
@@ -0,0 +1,13 @@
+// ----------------------------------------------------------------------
+//
+// Copyright (c) The Loxone.NET Authors. All rights reserved.
+//
+//
+// Use of this source code is governed by the MIT license that can be
+// found in the LICENSE.txt file.
+//
+// ----------------------------------------------------------------------
+
+using System.Runtime.CompilerServices;
+
+[assembly: InternalsVisibleTo("Loxone.Client.Tests")]
diff --git a/Loxone.Client/Strings.Designer.cs b/Loxone.Client/Strings.Designer.cs
index 597b6a6..88c3c76 100644
--- a/Loxone.Client/Strings.Designer.cs
+++ b/Loxone.Client/Strings.Designer.cs
@@ -78,15 +78,6 @@ internal static string HexConverter_BadFormat {
}
}
- ///
- /// Looks up a localized string similar to Invalid date format..
- ///
- internal static string LXDateTimeConverter_InvalidFormat {
- get {
- return ResourceManager.GetString("LXDateTimeConverter_InvalidFormat", resourceCulture);
- }
- }
-
///
/// Looks up a localized string similar to Miniserver command failed with status code {0}..
///
@@ -122,14 +113,5 @@ internal static string Uuid_ArgMustBeUuid {
return ResourceManager.GetString("Uuid_ArgMustBeUuid", resourceCulture);
}
}
-
- ///
- /// Looks up a localized string similar to Unexpected value when converting UUID..
- ///
- internal static string UuidConverter_UnexpectedValue {
- get {
- return ResourceManager.GetString("UuidConverter_UnexpectedValue", resourceCulture);
- }
- }
}
}
diff --git a/Loxone.Client/Strings.resx b/Loxone.Client/Strings.resx
index 864a78c..bedac54 100644
--- a/Loxone.Client/Strings.resx
+++ b/Loxone.Client/Strings.resx
@@ -123,9 +123,6 @@
Hex string format is invalid.
-
- Invalid date format.
-
Miniserver command failed with status code {0}.
@@ -135,9 +132,6 @@
Message received from Miniserver has invalid format.
-
- Unexpected value when converting UUID.
-
Argument must be of type Uuid.
diff --git a/Loxone.Client/StructureFile.cs b/Loxone.Client/StructureFile.cs
index 2d14f9a..919b0ff 100644
--- a/Loxone.Client/StructureFile.cs
+++ b/Loxone.Client/StructureFile.cs
@@ -96,16 +96,9 @@ public static async Task LoadAsync(string fileName, CancellationT
public static async Task LoadAsync(Stream stream, CancellationToken cancellationToken)
{
- using (var reader = new StreamReader(stream))
- {
- return await LoadAsync(reader, cancellationToken).ConfigureAwait(false);
- }
- }
-
- public static async Task LoadAsync(TextReader reader, CancellationToken cancellationToken)
- {
- string s = await reader.ReadToEndAsync().ConfigureAwait(false);
- return Parse(s);
+ var transportFile = await Transport.Serialization.SerializationHelper.DeserializeAsync(
+ stream, cancellationToken).ConfigureAwait(false);
+ return new StructureFile(transportFile);
}
public async Task SaveAsync(string fileName, CancellationToken cancellationToken)
@@ -116,19 +109,7 @@ public async Task SaveAsync(string fileName, CancellationToken cancellationToken
}
}
- public async Task SaveAsync(Stream stream, CancellationToken cancellationToken)
- {
- using (var writer = new StreamWriter(stream))
- {
- await SaveAsync(writer, cancellationToken).ConfigureAwait(false);
- }
- }
-
- public Task SaveAsync(TextWriter writer, CancellationToken cancellationToken)
- {
- var serializer = Transport.Serialization.SerializationHelper.CreateSerializer();
- serializer.Serialize(writer, _innerFile);
- return Task.FromResult(0);
- }
+ public Task SaveAsync(Stream stream, CancellationToken cancellationToken)
+ => Transport.Serialization.SerializationHelper.SerializeAsync(stream, _innerFile, cancellationToken);
}
}
diff --git a/Loxone.Client/Transport/Category.cs b/Loxone.Client/Transport/Category.cs
index cb7464b..333059f 100644
--- a/Loxone.Client/Transport/Category.cs
+++ b/Loxone.Client/Transport/Category.cs
@@ -1,4 +1,4 @@
-// ----------------------------------------------------------------------
+// ----------------------------------------------------------------------
//
// Copyright (c) The Loxone.NET Authors. All rights reserved.
//
@@ -12,35 +12,22 @@ namespace Loxone.Client.Transport
{
using System.Drawing;
using System.Collections.Generic;
- using Newtonsoft.Json;
- using Newtonsoft.Json.Linq;
+ using System.Text.Json;
+ using System.Text.Json.Serialization;
internal sealed class Category
{
- // Suppress 'Field is never assigned to, and will always have its
- // default value' warning.
- // Justification: Fields are set during deserialization.
- #pragma warning disable CS0649
-
- [JsonProperty("uuid")]
- public Uuid Uuid;
+ public Uuid Uuid { get; set; }
- [JsonProperty("name")]
- public string Name;
+ public string Name { get; set; }
- [JsonProperty("isFavorite")]
- public bool IsFavorite;
+ public bool IsFavorite { get; set; }
- [JsonProperty("defaultRating")]
- public int DefaultRating;
+ public int DefaultRating { get; set; }
- // There should be support for Color type in .NET Standard 1.7
- [JsonProperty("color")]
- public Color Color;
+ public Color Color { get; set; }
[JsonExtensionData]
- public IDictionary ExtensionData;
-
- #pragma warning restore CS0649
+ public IDictionary ExtensionData { get; set; }
}
}
diff --git a/Loxone.Client/Transport/Control.cs b/Loxone.Client/Transport/Control.cs
index c50ddf4..d5354e8 100644
--- a/Loxone.Client/Transport/Control.cs
+++ b/Loxone.Client/Transport/Control.cs
@@ -1,4 +1,4 @@
-// ----------------------------------------------------------------------
+// ----------------------------------------------------------------------
//
// Copyright (c) The Loxone.NET Authors. All rights reserved.
//
@@ -11,43 +11,31 @@
namespace Loxone.Client.Transport
{
using System.Collections.Generic;
- using Newtonsoft.Json;
- using Newtonsoft.Json.Linq;
+ using System.Text.Json;
+ using System.Text.Json.Serialization;
internal sealed class Control
{
- // Suppress 'Field is never assigned to, and will always have its
- // default value' warning.
- // Justification: Fields are set during deserialization.
- #pragma warning disable CS0649
-
- [JsonProperty("uuidAction")]
- public Uuid Uuid;
+ [JsonPropertyName("uuidAction")]
+ public Uuid Uuid { get; set; }
- [JsonProperty("name")]
- public string Name;
+ public string Name { get; set; }
- [JsonProperty("type")]
- public string ControlType;
+ [JsonPropertyName("type")]
+ public string ControlType { get; set; }
- [JsonProperty("isFavorite")]
- public bool IsFavorite;
+ public bool IsFavorite { get; set; }
- [JsonProperty("isSecured")]
- public bool IsSecured;
+ public bool IsSecured { get; set; }
- [JsonProperty("defaultRating")]
- public int DefaultRating;
+ public int DefaultRating { get; set; }
- [JsonProperty("room")]
- public Uuid? Room;
+ public Uuid? Room { get; set; }
- [JsonProperty("cat")]
- public Uuid? Category;
+ [JsonPropertyName("cat")]
+ public Uuid? Category { get; set; }
[JsonExtensionData]
- public IDictionary ExtensionData;
-
- #pragma warning restore CS0649
+ public IDictionary ExtensionData { get; set; }
}
}
diff --git a/Loxone.Client/Transport/Encryptor.cs b/Loxone.Client/Transport/Encryptor.cs
index 2be034f..78d2823 100644
--- a/Loxone.Client/Transport/Encryptor.cs
+++ b/Loxone.Client/Transport/Encryptor.cs
@@ -49,6 +49,7 @@ public string DecodeCommand(string command)
byte[] encryptedResponse = Convert.FromBase64String(command);
string decrypted = AesDecrypt(encryptedResponse);
+ decrypted = decrypted.TrimEnd('\0'); // Trim trailing zero padding.
return decrypted;
}
diff --git a/Loxone.Client/Transport/LXResponse.cs b/Loxone.Client/Transport/LXResponse.cs
index 4a05d77..29d4c9d 100644
--- a/Loxone.Client/Transport/LXResponse.cs
+++ b/Loxone.Client/Transport/LXResponse.cs
@@ -1,4 +1,4 @@
-// ----------------------------------------------------------------------
+// ----------------------------------------------------------------------
//
// Copyright (c) The Loxone.NET Authors. All rights reserved.
//
@@ -10,31 +10,21 @@
namespace Loxone.Client.Transport
{
- using Newtonsoft.Json;
+ using System.Text.Json.Serialization;
internal sealed class LXResponse
{
- // Suppress 'Field is never assigned to, and will always have its
- // default value' warning.
- // Justification: Fields are set during deserialization.
- #pragma warning disable CS0649
-
private struct Root
{
- [JsonProperty("LL")]
- public LXResponse Value;
+ [JsonPropertyName("LL")]
+ public LXResponse Value { get; set; }
}
- [JsonProperty("control")]
- public string Control;
-
- [JsonProperty("Code")]
- public int Code;
+ public string Control { get; set; }
- [JsonProperty("value")]
- public TValue Value;
+ public int Code { get; set; }
- #pragma warning restore CS0649
+ public TValue Value { get; set; }
public static LXResponse Deserialize(string s)
{
diff --git a/Loxone.Client/Transport/MiniserverInfo.cs b/Loxone.Client/Transport/MiniserverInfo.cs
index da7c165..56820c0 100644
--- a/Loxone.Client/Transport/MiniserverInfo.cs
+++ b/Loxone.Client/Transport/MiniserverInfo.cs
@@ -1,4 +1,4 @@
-// ----------------------------------------------------------------------
+// ----------------------------------------------------------------------
//
// Copyright (c) The Loxone.NET Authors. All rights reserved.
//
@@ -12,72 +12,53 @@ namespace Loxone.Client.Transport
{
using System;
using System.Collections.Generic;
+ using System.Text.Json;
+ using System.Text.Json.Serialization;
using Loxone.Client.Transport.Serialization;
- using Newtonsoft.Json;
- using Newtonsoft.Json.Linq;
internal sealed class MiniserverInfo
{
- // Suppress 'Field is never assigned to, and will always have its
- // default value' warning.
- // Justification: Fields are set during deserialization.
- #pragma warning disable CS0649
-
- [JsonProperty("serialNr")]
- public SerialNumber SerialNumber;
+ [JsonPropertyName("serialNr")]
+ public SerialNumber SerialNumber { get; set; }
- [JsonProperty("msName")]
- public string MiniserverName;
+ [JsonPropertyName("msName")]
+ public string MiniserverName { get; set; }
- [JsonProperty("projectName")]
- public string ProjectName;
+ public string ProjectName { get; set; }
- [JsonProperty("location")]
- public string Location;
+ public string Location { get; set; }
- [JsonProperty("heatPeriodStart")]
[JsonConverter(typeof(TimePeriodConverter))]
- public DateTime HeatPeriodStart;
+ public DateTime HeatPeriodStart { get; set; }
- [JsonProperty("heatPeriodEnd")]
[JsonConverter(typeof(TimePeriodConverter))]
- public DateTime HeatPeriodEnd;
+ public DateTime HeatPeriodEnd { get; set; }
- [JsonProperty("coolPeriodStart")]
[JsonConverter(typeof(TimePeriodConverter))]
- public DateTime CoolPeriodStart;
+ public DateTime CoolPeriodStart { get; set; }
- [JsonProperty("coolPeriodEnd")]
[JsonConverter(typeof(TimePeriodConverter))]
- public DateTime CoolPeriodEnd;
+ public DateTime CoolPeriodEnd { get; set; }
- [JsonProperty("catTitle")]
- public string CategoryTitle;
+ [JsonPropertyName("catTitle")]
+ public string CategoryTitle { get; set; }
- [JsonProperty("roomTitle")]
- public string RoomTitle;
+ public string RoomTitle { get; set; }
- [JsonProperty("miniserverType")]
- public int MiniserverType;
+ public int MiniserverType { get; set; }
- [JsonProperty("localUrl")]
- public string LocalUrl;
+ public string LocalUrl { get; set; }
- [JsonProperty("remoteUrl")]
- public string RemoteUrl;
+ public string RemoteUrl { get; set; }
- [JsonProperty("languageCode")]
- public string LanguageCode;
+ public string LanguageCode { get; set; }
- [JsonProperty("currency")]
- public string Currency;
+ public string Currency { get; set; }
- [JsonProperty("tempUnit")]
- public int TemperatureUnit;
+ [JsonPropertyName("tempUnit")]
+ public int TemperatureUnit { get; set; }
[JsonExtensionData]
- public IDictionary ExtensionData;
-
- #pragma warning restore CS0649
+ public IDictionary ExtensionData { get; set; }
}
}
diff --git a/Loxone.Client/Transport/Room.cs b/Loxone.Client/Transport/Room.cs
index 74c7cfb..6d6dcd1 100644
--- a/Loxone.Client/Transport/Room.cs
+++ b/Loxone.Client/Transport/Room.cs
@@ -1,4 +1,4 @@
-// ----------------------------------------------------------------------
+// ----------------------------------------------------------------------
//
// Copyright (c) The Loxone.NET Authors. All rights reserved.
//
@@ -11,31 +11,20 @@
namespace Loxone.Client.Transport
{
using System.Collections.Generic;
- using Newtonsoft.Json;
- using Newtonsoft.Json.Linq;
+ using System.Text.Json;
+ using System.Text.Json.Serialization;
internal sealed class Room
{
- // Suppress 'Field is never assigned to, and will always have its
- // default value' warning.
- // Justification: Fields are set during deserialization.
- #pragma warning disable CS0649
-
- [JsonProperty("uuid")]
- public Uuid Uuid;
+ public Uuid Uuid { get; set; }
- [JsonProperty("name")]
- public string Name;
+ public string Name { get; set; }
- [JsonProperty("isFavorite")]
- public bool IsFavorite;
+ public bool IsFavorite { get; set; }
- [JsonProperty("defaultRating")]
- public int DefaultRating;
+ public int DefaultRating { get; set; }
[JsonExtensionData]
- public IDictionary ExtensionData;
-
- #pragma warning restore CS0649
+ public IDictionary ExtensionData { get; set; }
}
}
diff --git a/Loxone.Client/Transport/Serialization/ColorConverter.cs b/Loxone.Client/Transport/Serialization/ColorConverter.cs
index ec3a871..6186369 100644
--- a/Loxone.Client/Transport/Serialization/ColorConverter.cs
+++ b/Loxone.Client/Transport/Serialization/ColorConverter.cs
@@ -13,29 +13,28 @@ namespace Loxone.Client.Transport.Serialization
using System;
using System.Drawing;
using System.Globalization;
- using Newtonsoft.Json;
+ using System.Text.Json;
+ using System.Text.Json.Serialization;
- internal sealed class ColorConverter : JsonConverter
+ internal sealed class ColorConverter : JsonConverter
{
- public override bool CanConvert(Type objectType) => objectType == typeof(Color);
-
- public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
+ public override Color Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
{
- string s = reader.Value as string;
+ string s = reader.GetString();
if (s == null || s.Length != 7 || s[0] != '#')
{
- throw new JsonSerializationException(Strings.ColorConverter_InvalidFormat);
+ throw new FormatException(Strings.ColorConverter_InvalidFormat);
}
- var rgb = int.Parse(s.Substring(1), NumberStyles.AllowHexSpecifier, CultureInfo.InvariantCulture);
+ int rgb = Int32.Parse(s.Substring(1), NumberStyles.AllowHexSpecifier, CultureInfo.InvariantCulture);
return Color.FromArgb(rgb | unchecked((int)0xFF000000));
}
- public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
+ public override void Write(Utf8JsonWriter writer, Color value, JsonSerializerOptions options)
{
- var rgb = ((Color)value).ToArgb();
- writer.WriteValue(
- string.Concat(
+ int rgb = value.ToArgb();
+ writer.WriteStringValue(
+ String.Concat(
"#",
((rgb >> 16) & 0xFF).ToString("X2", CultureInfo.InvariantCulture),
((rgb >> 8) & 0xFF).ToString("X2", CultureInfo.InvariantCulture),
diff --git a/Loxone.Client/Transport/Serialization/FormattedDateTimeConverter.cs b/Loxone.Client/Transport/Serialization/FormattedDateTimeConverter.cs
new file mode 100644
index 0000000..ee8514b
--- /dev/null
+++ b/Loxone.Client/Transport/Serialization/FormattedDateTimeConverter.cs
@@ -0,0 +1,33 @@
+// ----------------------------------------------------------------------
+//
+// Copyright (c) The Loxone.NET Authors. All rights reserved.
+//
+//
+// Use of this source code is governed by the MIT license that can be
+// found in the LICENSE.txt file.
+//
+// ----------------------------------------------------------------------
+
+namespace Loxone.Client.Transport.Serialization
+{
+ using System;
+ using System.Globalization;
+ using System.Text.Json;
+ using System.Text.Json.Serialization;
+
+ internal class FormattedDateTimeConverter : JsonConverter
+ {
+ private readonly string _format;
+
+ public FormattedDateTimeConverter(string format)
+ {
+ this._format = format;
+ }
+
+ public override DateTime Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
+ => DateTime.ParseExact(reader.GetString(), _format, CultureInfo.InvariantCulture);
+
+ public override void Write(Utf8JsonWriter writer, DateTime value, JsonSerializerOptions options)
+ => writer.WriteStringValue(value.ToString(_format, CultureInfo.InvariantCulture));
+ }
+}
diff --git a/Loxone.Client/Transport/Serialization/JsonWithinStringConverter.cs b/Loxone.Client/Transport/Serialization/JsonWithinStringConverter.cs
index 4dad46a..6b9fc5d 100644
--- a/Loxone.Client/Transport/Serialization/JsonWithinStringConverter.cs
+++ b/Loxone.Client/Transport/Serialization/JsonWithinStringConverter.cs
@@ -1,4 +1,4 @@
-// ----------------------------------------------------------------------
+// ----------------------------------------------------------------------
//
// Copyright (c) The Loxone.NET Authors. All rights reserved.
//
@@ -11,61 +11,18 @@
namespace Loxone.Client.Transport.Serialization
{
using System;
- using System.Collections.Generic;
- using System.Diagnostics.Contracts;
- using System.Linq;
- using Newtonsoft.Json;
+ using System.Text.Json;
+ using System.Text.Json.Serialization;
- internal sealed class JsonWithinStringConverter : JsonConverter
+ internal sealed class JsonWithinStringConverter : JsonConverter
{
- private HashSet _targetTypes;
-
- public ICollection TargetTypes
- {
- get { return _targetTypes; }
- }
-
- public JsonWithinStringConverter()
- {
- _targetTypes = new HashSet();
- }
-
- public JsonWithinStringConverter(Type targetType)
- : this()
- {
- Contract.Requires(targetType != null);
-
- _targetTypes.Add(targetType);
- }
-
- public JsonWithinStringConverter(params Type[] targetTypes)
- {
- Contract.Requires(targetTypes != null);
-
- foreach (var type in targetTypes)
- {
- _targetTypes.Add(type);
- }
- }
-
- public override bool CanConvert(Type objectType) => _targetTypes.Contains(objectType);
-
- public override bool CanRead => true;
-
- public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
+ public override T Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
{
- string s = reader.Value as string;
- return JsonConvert.DeserializeObject(
- s,
- objectType,
- serializer.Converters
- .Where(c => c.GetType() != typeof(JsonWithinStringConverter))
- .ToArray());
+ string s = reader.GetString();
+ s = s.Replace('\'', '"');
+ return JsonSerializer.Deserialize(s, SerializationHelper.DefaultOptions);
}
- public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
- {
- throw new NotImplementedException();
- }
+ public override void Write(Utf8JsonWriter writer, T value, JsonSerializerOptions options) => new NotSupportedException();
}
}
diff --git a/Loxone.Client/Transport/Serialization/LXDateTimeConverter.cs b/Loxone.Client/Transport/Serialization/LXDateTimeConverter.cs
index 6cff6ed..2cd0456 100644
--- a/Loxone.Client/Transport/Serialization/LXDateTimeConverter.cs
+++ b/Loxone.Client/Transport/Serialization/LXDateTimeConverter.cs
@@ -11,32 +11,17 @@
namespace Loxone.Client.Transport.Serialization
{
using System;
- using Newtonsoft.Json;
+ using System.Text.Json;
+ using System.Text.Json.Serialization;
- internal sealed class LXDateTimeConverter : JsonConverter
+ internal sealed class LXDateTimeConverter : JsonConverter
{
private static readonly DateTime _epochStart = new DateTime(2009, 1, 1, 0, 0, 0, DateTimeKind.Local);
- public override bool CanConvert(Type objectType) => objectType == typeof(DateTime);
+ public override DateTime Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
+ => FromDouble(reader.GetDouble());
- public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
- {
- switch (reader.Value)
- {
- case long l:
- return FromInt64(l);
- case double d:
- return FromDouble(d);
- default:
- throw new JsonSerializationException(Strings.LXDateTimeConverter_InvalidFormat);
- }
- }
-
- private static DateTime FromInt64(long l) => FromDouble(l);
-
- private static DateTime FromDouble(double d) => _epochStart.AddSeconds(d);
-
- public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
+ public override void Write(Utf8JsonWriter writer, DateTime value, JsonSerializerOptions options)
{
var date = (DateTime)value;
@@ -44,7 +29,9 @@ public override void WriteJson(JsonWriter writer, object value, JsonSerializer s
date = date.ToLocalTime();
double d = date.Subtract(_epochStart).TotalSeconds;
- writer.WriteValue(d);
+ writer.WriteNumberValue(d);
}
+
+ private static DateTime FromDouble(double d) => _epochStart.AddSeconds(d);
}
}
diff --git a/Loxone.Client/Transport/Serialization/QuotedInt32Converter.cs b/Loxone.Client/Transport/Serialization/QuotedInt32Converter.cs
new file mode 100644
index 0000000..2c7543f
--- /dev/null
+++ b/Loxone.Client/Transport/Serialization/QuotedInt32Converter.cs
@@ -0,0 +1,36 @@
+// ----------------------------------------------------------------------
+//
+// Copyright (c) The Loxone.NET Authors. All rights reserved.
+//
+//
+// Use of this source code is governed by the MIT license that can be
+// found in the LICENSE.txt file.
+//
+// ----------------------------------------------------------------------
+
+namespace Loxone.Client.Transport.Serialization
+{
+ using System;
+ using System.Globalization;
+ using System.Text.Json;
+ using System.Text.Json.Serialization;
+
+ ///
+ /// https://github.com/dotnet/corefx/issues/39473
+ ///
+ internal sealed class QuotedInt32Converter : JsonConverter
+ {
+ public override int Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
+ {
+ if (reader.TokenType == JsonTokenType.String)
+ {
+ return Int32.Parse(reader.GetString(), CultureInfo.InvariantCulture);
+ }
+
+ return reader.GetInt32();
+ }
+
+ public override void Write(Utf8JsonWriter writer, int value, JsonSerializerOptions options)
+ => writer.WriteStringValue(value.ToString(CultureInfo.InvariantCulture));
+ }
+}
diff --git a/Loxone.Client/Transport/Serialization/Responses/Api.cs b/Loxone.Client/Transport/Serialization/Responses/Api.cs
index d9b2099..1fcdcba 100644
--- a/Loxone.Client/Transport/Serialization/Responses/Api.cs
+++ b/Loxone.Client/Transport/Serialization/Responses/Api.cs
@@ -1,4 +1,4 @@
-// ----------------------------------------------------------------------
+// ----------------------------------------------------------------------
//
// Copyright (c) The Loxone.NET Authors. All rights reserved.
//
@@ -11,21 +11,13 @@
namespace Loxone.Client.Transport.Serialization.Responses
{
using System;
- using Newtonsoft.Json;
+ using System.Text.Json.Serialization;
internal sealed class Api
{
- // Suppress 'Field is never assigned to, and will always have its
- // default value' warning.
- // Justification: Fields are set during deserialization.
- #pragma warning disable CS0649
-
- [JsonProperty("snr")]
- public SerialNumber SerialNumber;
-
- [JsonProperty("version")]
- public Version Version;
+ [JsonPropertyName("snr")]
+ public SerialNumber SerialNumber { get; set; }
- #pragma warning restore CS0649
+ public Version Version { get; set; }
}
}
diff --git a/Loxone.Client/Transport/Serialization/Responses/GetKey2.cs b/Loxone.Client/Transport/Serialization/Responses/GetKey2.cs
index 1b20c75..a3e91ed 100644
--- a/Loxone.Client/Transport/Serialization/Responses/GetKey2.cs
+++ b/Loxone.Client/Transport/Serialization/Responses/GetKey2.cs
@@ -1,4 +1,4 @@
-// ----------------------------------------------------------------------
+// ----------------------------------------------------------------------
//
// Copyright (c) The Loxone.NET Authors. All rights reserved.
//
@@ -10,24 +10,15 @@
namespace Loxone.Client.Transport.Serialization.Responses
{
- using Newtonsoft.Json;
+ using System.Text.Json.Serialization;
internal sealed class GetKey2
{
- // Suppress 'Field is never assigned to, and will always have its
- // default value' warning.
- // Justification: Fields are set during deserialization.
- #pragma warning disable CS0649
-
- [JsonProperty("key")]
- public string Key;
-
- [JsonProperty("salt")]
- public string Salt;
+ public string Key { get; set; }
- [JsonProperty("hashAlg")]
- public string HashAlgorithm;
+ public string Salt { get; set; }
- #pragma warning restore CS0649
+ [JsonPropertyName("hashAlg")]
+ public string HashAlgorithm { get; set; }
}
}
diff --git a/Loxone.Client/Transport/Serialization/Responses/GetToken.cs b/Loxone.Client/Transport/Serialization/Responses/GetToken.cs
index c2b7dae..49486fb 100644
--- a/Loxone.Client/Transport/Serialization/Responses/GetToken.cs
+++ b/Loxone.Client/Transport/Serialization/Responses/GetToken.cs
@@ -11,31 +11,20 @@
namespace Loxone.Client.Transport.Serialization.Responses
{
using System;
- using Newtonsoft.Json;
+ using System.Text.Json.Serialization;
internal sealed class GetToken
{
- // Suppress 'Field is never assigned to, and will always have its
- // default value' warning.
- // Justification: Fields are set during deserialization.
- #pragma warning disable CS0649
+ public string Token { get; set; }
- [JsonProperty("token")]
- public string Token;
+ public string Key { get; set; }
- [JsonProperty("key")]
- public string Key;
-
- [JsonProperty("validUntil")]
[JsonConverter(typeof(LXDateTimeConverter))]
- public DateTime ValidUntil;
-
- [JsonProperty("tokenRights")]
- public int TokenRights;
+ public DateTime ValidUntil { get; set; }
- [JsonProperty("unsecurePass")]
- public bool UnsecurePassword;
+ public int TokenRights { get; set; }
- #pragma warning restore CS0649
+ [JsonPropertyName("unsecurePass")]
+ public bool UnsecurePassword { get; set; }
}
}
diff --git a/Loxone.Client/Transport/Serialization/SerialNumberConverter.cs b/Loxone.Client/Transport/Serialization/SerialNumberConverter.cs
index c0e17b4..4b0fe3b 100644
--- a/Loxone.Client/Transport/Serialization/SerialNumberConverter.cs
+++ b/Loxone.Client/Transport/Serialization/SerialNumberConverter.cs
@@ -1,4 +1,4 @@
-// ----------------------------------------------------------------------
+// ----------------------------------------------------------------------
//
// Copyright (c) The Loxone.NET Authors. All rights reserved.
//
@@ -11,22 +11,15 @@
namespace Loxone.Client.Transport.Serialization
{
using System;
- using Newtonsoft.Json;
+ using System.Text.Json;
+ using System.Text.Json.Serialization;
- internal sealed class SerialNumberConverter : JsonConverter
+ internal sealed class SerialNumberConverter : JsonConverter
{
- public override bool CanConvert(Type objectType) => objectType == typeof(SerialNumber);
-
- public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
- {
- string s = reader.Value as string;
- return SerialNumber.Parse(s);
- }
+ public override SerialNumber Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
+ => SerialNumber.Parse(reader.GetString());
- public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
- {
- // Just use SerialNumber.ToString method.
- writer.WriteValue(value.ToString());
- }
+ public override void Write(Utf8JsonWriter writer, SerialNumber value, JsonSerializerOptions options)
+ => writer.WriteStringValue(value.ToString());
}
}
diff --git a/Loxone.Client/Transport/Serialization/SerializationHelper.cs b/Loxone.Client/Transport/Serialization/SerializationHelper.cs
index a0721e3..eab1d45 100644
--- a/Loxone.Client/Transport/Serialization/SerializationHelper.cs
+++ b/Loxone.Client/Transport/Serialization/SerializationHelper.cs
@@ -1,4 +1,4 @@
-// ----------------------------------------------------------------------
+// ----------------------------------------------------------------------
//
// Copyright (c) The Loxone.NET Authors. All rights reserved.
//
@@ -11,47 +11,61 @@
namespace Loxone.Client.Transport.Serialization
{
using System.IO;
- using Newtonsoft.Json;
+ using System.Text.Encodings.Web;
+ using System.Text.Json;
+ using System.Text.Json.Serialization;
+ using System.Threading;
+ using System.Threading.Tasks;
using Loxone.Client.Transport.Serialization.Responses;
internal static class SerializationHelper
{
- private static JsonConverter[] _converters = new JsonConverter[]
+ private static readonly JsonConverter[] _defaultConvertes = new JsonConverter[]
{
- new JsonWithinStringConverter(typeof(Api)),
new SerialNumberConverter(),
new UuidConverter(),
new ColorConverter(),
+ new VersionConverter(),
+ new QuotedInt32Converter(),
+ new FormattedDateTimeConverter("yyyy'-'MM'-'dd' 'HH':'mm':'ss"),
+
};
- public static JsonSerializer CreateSerializer()
+ private static readonly JsonSerializerOptions _options = InitializeOptions();
+
+ internal static readonly JsonSerializerOptions DefaultOptions = CreateDefaultOptions();
+
+ private static JsonSerializerOptions CreateDefaultOptions()
{
- var serializer = new JsonSerializer();
- serializer.DateFormatString = "yyyy'-'MM'-'dd' 'HH':'mm':'ss";
+ var options = new JsonSerializerOptions
+ {
+ PropertyNameCaseInsensitive = true,
+ WriteIndented = true,
+ Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping,
+ PropertyNamingPolicy = JsonNamingPolicy.CamelCase
+ };
- for (int i = 0; i < _converters.Length; i++)
+ for (int i = 0; i < _defaultConvertes.Length; i++)
{
- serializer.Converters.Add(_converters[i]);
+ options.Converters.Add(_defaultConvertes[i]);
}
- return serializer;
+ return options;
}
- public static T Deserialize(TextReader reader)
+ private static JsonSerializerOptions InitializeOptions()
{
- var serializer = CreateSerializer();
- using (var jsonReader = new JsonTextReader(reader) { CloseInput = false })
- {
- return serializer.Deserialize(jsonReader);
- }
+ var options = CreateDefaultOptions();
+ options.Converters.Add(new JsonWithinStringConverter());
+ return options;
}
- public static T Deserialize(string s)
- {
- using (var reader = new StringReader(s))
- {
- return Deserialize(reader);
- }
- }
+ public static T Deserialize(string s) => JsonSerializer.Deserialize(s, _options);
+
+ public static ValueTask DeserializeAsync(Stream stream, CancellationToken cancellationToken)
+ => JsonSerializer.DeserializeAsync(stream, _options, cancellationToken);
+
+ public static Task SerializeAsync(Stream stream, T value, CancellationToken cancellationToken)
+ => JsonSerializer.SerializeAsync(stream, value, _options, cancellationToken);
}
}
diff --git a/Loxone.Client/Transport/Serialization/TimePeriodConverter.cs b/Loxone.Client/Transport/Serialization/TimePeriodConverter.cs
index fc13ecb..372f821 100644
--- a/Loxone.Client/Transport/Serialization/TimePeriodConverter.cs
+++ b/Loxone.Client/Transport/Serialization/TimePeriodConverter.cs
@@ -1,4 +1,4 @@
-// ----------------------------------------------------------------------
+// ----------------------------------------------------------------------
//
// Copyright (c) The Loxone.NET Authors. All rights reserved.
//
@@ -10,25 +10,10 @@
namespace Loxone.Client.Transport.Serialization
{
- using System;
- using Newtonsoft.Json;
- using Newtonsoft.Json.Converters;
-
- internal sealed class TimePeriodConverter : IsoDateTimeConverter
+ internal sealed class TimePeriodConverter : FormattedDateTimeConverter
{
- public TimePeriodConverter()
- {
- DateTimeFormat = "MM'-'dd";
- }
-
- public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
- {
- return base.ReadJson(reader, objectType, existingValue, serializer);
- }
-
- public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
+ public TimePeriodConverter() : base("MM'-'dd")
{
- base.WriteJson(writer, value, serializer);
}
}
}
diff --git a/Loxone.Client/Transport/Serialization/UuidConverter.cs b/Loxone.Client/Transport/Serialization/UuidConverter.cs
index 007ebbd..9db73f7 100644
--- a/Loxone.Client/Transport/Serialization/UuidConverter.cs
+++ b/Loxone.Client/Transport/Serialization/UuidConverter.cs
@@ -1,4 +1,4 @@
-// ----------------------------------------------------------------------
+// ----------------------------------------------------------------------
//
// Copyright (c) The Loxone.NET Authors. All rights reserved.
//
@@ -11,31 +11,15 @@
namespace Loxone.Client.Transport.Serialization
{
using System;
- using Newtonsoft.Json;
+ using System.Text.Json;
+ using System.Text.Json.Serialization;
- internal sealed class UuidConverter : JsonConverter
+ internal sealed class UuidConverter : JsonConverter
{
- public override bool CanConvert(Type objectType) => objectType == typeof(Uuid) || objectType == typeof(Uuid?);
-
- public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
- {
- bool nullable = objectType == typeof(Uuid?);
-
- if (reader.TokenType == JsonToken.Null && nullable)
- {
- return null;
- }
- else if (reader.TokenType == JsonToken.String)
- {
- return Uuid.Parse(reader.Value as string);
- }
-
- throw new JsonSerializationException(Strings.UuidConverter_UnexpectedValue);
- }
+ public override Uuid Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
+ => Uuid.Parse(reader.GetString());
- public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
- {
- writer.WriteValue(value.ToString());
- }
+ public override void Write(Utf8JsonWriter writer, Uuid value, JsonSerializerOptions options)
+ => writer.WriteStringValue(value.ToString());
}
}
diff --git a/Loxone.Client/Transport/Serialization/VersionConverter.cs b/Loxone.Client/Transport/Serialization/VersionConverter.cs
new file mode 100644
index 0000000..223c1d9
--- /dev/null
+++ b/Loxone.Client/Transport/Serialization/VersionConverter.cs
@@ -0,0 +1,25 @@
+// ----------------------------------------------------------------------
+//
+// Copyright (c) The Loxone.NET Authors. All rights reserved.
+//
+//
+// Use of this source code is governed by the MIT license that can be
+// found in the LICENSE.txt file.
+//
+// ----------------------------------------------------------------------
+
+namespace Loxone.Client.Transport.Serialization
+{
+ using System;
+ using System.Text.Json;
+ using System.Text.Json.Serialization;
+
+ internal sealed class VersionConverter : JsonConverter
+ {
+ public override Version Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
+ => Version.Parse(reader.GetString());
+
+ public override void Write(Utf8JsonWriter writer, Version value, JsonSerializerOptions options)
+ => writer.WriteStringValue(value.ToString());
+ }
+}
diff --git a/Loxone.Client/Transport/StructureFile.cs b/Loxone.Client/Transport/StructureFile.cs
index cc3b328..41a2454 100644
--- a/Loxone.Client/Transport/StructureFile.cs
+++ b/Loxone.Client/Transport/StructureFile.cs
@@ -1,4 +1,4 @@
-// ----------------------------------------------------------------------
+// ----------------------------------------------------------------------
//
// Copyright (c) The Loxone.NET Authors. All rights reserved.
//
@@ -12,34 +12,24 @@ namespace Loxone.Client.Transport
{
using System;
using System.Collections.Generic;
- using Newtonsoft.Json;
- using Newtonsoft.Json.Linq;
+ using System.Text.Json;
+ using System.Text.Json.Serialization;
internal sealed class StructureFile
{
- // Suppress 'Field is never assigned to, and will always have its
- // default value' warning.
- // Justification: Fields are set during deserialization.
- #pragma warning disable CS0649
-
- [JsonProperty("lastModified")]
- public DateTime LastModified;
+ public DateTime LastModified { get; set; }
- [JsonProperty("msInfo")]
- public MiniserverInfo MiniserverInfo;
+ [JsonPropertyName("msInfo")]
+ public MiniserverInfo MiniserverInfo { get; set; }
- [JsonProperty("rooms")]
- public IDictionary Rooms;
+ public IDictionary Rooms { get; set; }
- [JsonProperty("cats")]
- public IDictionary Categories;
+ [JsonPropertyName("cats")]
+ public IDictionary Categories { get; set; }
- [JsonProperty("controls")]
- public IDictionary Controls;
+ public IDictionary Controls { get; set; }
[JsonExtensionData]
- public IDictionary ExtensionData;
-
- #pragma warning restore CS0649
+ public IDictionary ExtensionData { get; set; }
}
}
diff --git a/Loxone.NET.sln b/Loxone.NET.sln
index cee8b54..cb8e1c8 100644
--- a/Loxone.NET.sln
+++ b/Loxone.NET.sln
@@ -9,10 +9,13 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Loxone.Client.Samples.Conso
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{BEFE450E-D373-433D-AD42-467D60454643}"
ProjectSection(SolutionItems) = preProject
+ .editorconfig = .editorconfig
LICENSE.txt = LICENSE.txt
README.md = README.md
EndProjectSection
EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Loxone.Client.Tests", "Loxone.Client.Tests\Loxone.Client.Tests.csproj", "{A007996A-5192-4128-A955-21CAB9CF4DF3}"
+EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@@ -27,6 +30,10 @@ Global
{CB195BC9-96DD-4845-81CE-BF2F0E2F5D3F}.Debug|Any CPU.Build.0 = Debug|Any CPU
{CB195BC9-96DD-4845-81CE-BF2F0E2F5D3F}.Release|Any CPU.ActiveCfg = Release|Any CPU
{CB195BC9-96DD-4845-81CE-BF2F0E2F5D3F}.Release|Any CPU.Build.0 = Release|Any CPU
+ {A007996A-5192-4128-A955-21CAB9CF4DF3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {A007996A-5192-4128-A955-21CAB9CF4DF3}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {A007996A-5192-4128-A955-21CAB9CF4DF3}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {A007996A-5192-4128-A955-21CAB9CF4DF3}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE