diff --git a/src/Couchbase.Lite.Shared/API/DI/Service.cs b/src/Couchbase.Lite.Shared/API/DI/Service.cs index 0e00ad731..281b8b3a7 100644 --- a/src/Couchbase.Lite.Shared/API/DI/Service.cs +++ b/src/Couchbase.Lite.Shared/API/DI/Service.cs @@ -30,7 +30,7 @@ #endif using LiteCore.Interop; -using SimpleInjector; +using Microsoft.Extensions.DependencyInjection; namespace Couchbase.Lite.DI; @@ -39,25 +39,24 @@ namespace Couchbase.Lite.DI; /// public static class Service { - private static readonly Container ServiceCollection = new(); + public static readonly ServiceProvider Provider; [ExcludeFromCodeCoverage] static Service() { - ServiceCollection.Options.AllowOverridingRegistrations = true; - + var collection = new ServiceCollection(); #if CBL_PLATFORM_DOTNET || CBL_PLATFORM_DOTNETFX - AutoRegister(typeof(Database).GetTypeInfo().Assembly); + AutoRegister(typeof(Database).GetTypeInfo().Assembly, collection); if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) { - Register(new WindowsProxy()); + collection.AddSingleton(new WindowsProxy()); } else if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX)) { - Register(new MacProxy()); + collection.AddSingleton(new MacProxy()); } else if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux)) { - Register(new LinuxProxy()); + collection.AddSingleton(new LinuxProxy()); } #elif CBL_PLATFORM_WINUI - AutoRegister(typeof(Database).GetTypeInfo().Assembly); + AutoRegister(typeof(Database).GetTypeInfo().Assembly, collection); #elif CBL_PLATFORM_ANDROID #if !TEST_COVERAGE if (Droid.Context == null) { @@ -65,15 +64,17 @@ static Service() "Android context not set. Please ensure that a call to Couchbase.Lite.Support.Droid.Activate() is made."); } - AutoRegister(typeof(Database).Assembly); - Register(() => new DefaultDirectoryResolver(Droid.Context)); - Register(() => new MainThreadTaskScheduler(Droid.Context)); + AutoRegister(typeof(Database).Assembly, collection); + collection.AddSingleton(_ => new DefaultDirectoryResolver(Droid.Context)); + collection.AddSingleton(_ => new MainThreadTaskScheduler(Droid.Context)); #endif #elif CBL_PLATFORM_APPLE - AutoRegister(typeof(Database).Assembly); + AutoRegister(typeof(Database).Assembly, collection); #else #error Unknown Platform #endif + + Provider = collection.BuildServiceProvider(); } /// @@ -81,12 +82,13 @@ static Service() /// s. To auto register classes, /// they must implement an interface and must have a default constructor. /// - /// + /// The assembly to scan + /// The collection to add to /// Thrown if is null /// Thrown if an invalid type is found inside the assembly (i.e. /// one that does not implement any interfaces and/or does not have a parameter-less constructor) [ExcludeFromCodeCoverage] - private static void AutoRegister(Assembly assembly) + private static void AutoRegister(Assembly assembly, IServiceCollection serviceCollection) { if (assembly == null) throw new ArgumentNullException(nameof(assembly)); @@ -117,82 +119,15 @@ private static void AutoRegister(Assembly assembly) } if (attribute.Transient) { - ServiceCollection.Register(interfaceType, type, Lifestyle.Transient); + serviceCollection.AddTransient(interfaceType, type); } else { if (attribute.Lazy) { - ServiceCollection.Register(interfaceType, type, Lifestyle.Singleton); + serviceCollection.AddSingleton(interfaceType, type); } else { - ServiceCollection.RegisterInstance(interfaceType, Activator.CreateInstance(type) + serviceCollection.AddSingleton(interfaceType, Activator.CreateInstance(type) ?? throw new CouchbaseLiteException(C4ErrorCode.UnexpectedError, $"Unable to create instance of {type.Name}")); } } } } - - /// - /// Registers an implementation for the given service - /// - /// The service type - /// The implementation type - /// If true each call to will return - /// a new instance, otherwise use a singleton - [ExcludeFromCodeCoverage] - [SuppressMessage("ReSharper", "UnusedMember.Global")] - public static void Register(bool transient = false) where TService : class where TImplementation : class, TService - { - Lifestyle style = transient ? Lifestyle.Transient : Lifestyle.Singleton; - ServiceCollection.Register(style); - } - - /// - /// Registers a lazy implementation for the given service - /// - /// The service type - /// The function that creates the object to use - /// If true each call to will return - /// a new instance, otherwise use a singleton - [ExcludeFromCodeCoverage] - [SuppressMessage("ReSharper", "UnusedMember.Global")] - public static void Register(Func generator, bool transient = false) where TService : class - { - var style = transient ? Lifestyle.Transient : Lifestyle.Singleton; - ServiceCollection.Register(generator, style); - } - - /// - /// Registers an instantiated object as a singleton implementation for a service - /// - /// The service type - /// The singleton instance to use as the implementation - [ExcludeFromCodeCoverage] - [SuppressMessage("ReSharper", "MemberCanBePrivate.Global")] - public static void Register(TService instance) - where TService : class - { - ServiceCollection.RegisterInstance(instance); - } - - /// - /// Gets the implementation for the given service, or null - /// if no implementation is registered - /// - /// The type of service to get an implementation for - /// The implementation for the given service - public static T? GetInstance() where T : class - { - try { - return ServiceCollection.GetInstance(); - } catch (ActivationException) { - return null; - } - } - - internal static T GetRequiredInstance() where T : class - { - return GetInstance() ?? throw new InvalidOperationException( - $""" - A required dependency injection class is missing ({typeof(T).FullName}). - If this is not a custom platform, please file a bug report at https://github.com/couchbase/couchbase-lite-net/issues - """); - } } \ No newline at end of file diff --git a/src/Couchbase.Lite.Shared/API/Database/Collection.cs b/src/Couchbase.Lite.Shared/API/Database/Collection.cs index 9c1feffc5..cabe141a1 100644 --- a/src/Couchbase.Lite.Shared/API/Database/Collection.cs +++ b/src/Couchbase.Lite.Shared/API/Database/Collection.cs @@ -26,13 +26,13 @@ using LiteCore; using LiteCore.Interop; using LiteCore.Util; -using Newtonsoft.Json; using System; using System.Collections.Generic; using System.Diagnostics; using System.Diagnostics.CodeAnalysis; using System.Runtime.InteropServices; using System.Threading.Tasks; +using System.Text.Json; namespace Couchbase.Lite; @@ -534,7 +534,7 @@ public void CreateIndex(string name, IIndex index) CheckCollectionValid(); var concreteIndex = Misc.TryCast(index); var jsonObj = concreteIndex.ToJSON(); - var json = JsonConvert.SerializeObject(jsonObj); + var json = JsonSerializer.Serialize(jsonObj); LiteCoreBridge.Check(err => { var internalOpts = concreteIndex.Options; diff --git a/src/Couchbase.Lite.Shared/API/Database/Database.cs b/src/Couchbase.Lite.Shared/API/Database/Database.cs index 5d6f2edb9..cd90a8321 100644 --- a/src/Couchbase.Lite.Shared/API/Database/Database.cs +++ b/src/Couchbase.Lite.Shared/API/Database/Database.cs @@ -37,6 +37,7 @@ using System.Collections.Concurrent; using System.Diagnostics; using System.Diagnostics.CodeAnalysis; +using Microsoft.Extensions.DependencyInjection; namespace Couchbase.Lite; @@ -761,7 +762,7 @@ internal void CheckOpenLocked() private static string DatabasePath(string? directory) { var directoryToUse = String.IsNullOrWhiteSpace(directory) - ? Service.GetRequiredInstance().DefaultDirectory() + ? Service.Provider.GetRequiredService().DefaultDirectory() : directory; if (String.IsNullOrWhiteSpace(directoryToUse)) { diff --git a/src/Couchbase.Lite.Shared/API/Database/DatabaseConfiguration.cs b/src/Couchbase.Lite.Shared/API/Database/DatabaseConfiguration.cs index f69b934ee..aafdba317 100644 --- a/src/Couchbase.Lite.Shared/API/Database/DatabaseConfiguration.cs +++ b/src/Couchbase.Lite.Shared/API/Database/DatabaseConfiguration.cs @@ -20,6 +20,7 @@ using Couchbase.Lite.Info; using Couchbase.Lite.Internal.Logging; using Couchbase.Lite.Util; +using Microsoft.Extensions.DependencyInjection; #if NET8_0_OR_GREATER using System.Runtime.Versioning; @@ -41,7 +42,7 @@ public sealed partial record DatabaseConfiguration /// public string Directory { - get => _directory ?? Service.GetRequiredInstance().DefaultDirectory(); + get => _directory ?? Service.Provider.GetRequiredService().DefaultDirectory(); init => _directory = CBDebug.MustNotBeNull(WriteLog.To.Database, Tag, "Directory", value); } diff --git a/src/Couchbase.Lite.Shared/API/Document/ArrayObject.cs b/src/Couchbase.Lite.Shared/API/Document/ArrayObject.cs index bde222e80..16c0410b8 100644 --- a/src/Couchbase.Lite.Shared/API/Document/ArrayObject.cs +++ b/src/Couchbase.Lite.Shared/API/Document/ArrayObject.cs @@ -19,14 +19,14 @@ using System; using System.Collections; using System.Collections.Generic; - +using System.Text.Json; +using System.Text.Json.Serialization; using Couchbase.Lite.Fleece; using Couchbase.Lite.Internal.Doc; using Couchbase.Lite.Internal.Serialization; using Couchbase.Lite.Support; using LiteCore; using LiteCore.Interop; -using Newtonsoft.Json; namespace Couchbase.Lite; @@ -171,66 +171,62 @@ IEnumerator IEnumerable.GetEnumerator() } [System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage] -internal sealed class IArrayConverter : JsonConverter +internal sealed class IArrayConverter : JsonConverter { - public override void WriteJson(JsonWriter writer, object? value, JsonSerializer serializer) - { - var arr = value as IArray ?? throw new CouchbaseLiteException(C4ErrorCode.UnexpectedError, - "Invalid input received in WriteJson (not IArray)"); - writer.WriteStartArray(); - foreach (var item in arr) { - serializer.Serialize(writer, item); - } - - writer.WriteEndArray(); - } - - public override object ReadJson(JsonReader reader, Type objectType, object? existingValue, JsonSerializer serializer) + public override IArray Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) { var arr = new MutableArrayObject(); - while (reader.Read()) { + + while (reader.Read() && reader.TokenType != JsonTokenType.EndArray) { switch (reader.TokenType) { - case JsonToken.EndObject: + case JsonTokenType.StartObject: { - var val = serializer.Deserialize(reader) as IDictionary - ?? throw new CouchbaseLiteException(C4ErrorCode.UnexpectedError, "Corrupt input received in ReadJson (EndObject != IDictionary)"); - if (val.TryGetValue(Constants.ObjectTypeProperty, out var value) && - value.Equals(Constants.ObjectTypeBlob)) { - var blob = serializer.Deserialize(reader) - ?? throw new CouchbaseLiteException(C4ErrorCode.UnexpectedError, "Error deserializing blob in ReadJson (IArray)"); + using var document = JsonDocument.ParseValue(ref reader); + var element = document.RootElement; + if (element.TryGetProperty(Constants.ObjectTypeProperty, out var prop) + && prop.GetString() == Constants.ObjectTypeBlob) { + var blob = element.Deserialize(options) ?? + throw new CouchbaseLiteException(C4ErrorCode.UnexpectedError, + "Error deserializing blob in ReadJson (IArray)"); arr.AddBlob(blob); - } else { - var dict = serializer.Deserialize(reader) - ?? throw new CouchbaseLiteException(C4ErrorCode.UnexpectedError, "Error deserializing dict in ReadJson (IArray)"); + } + else { + var dict = element.Deserialize(options) ?? + throw new CouchbaseLiteException(C4ErrorCode.UnexpectedError, + "Error deserializing dict in ReadJson (IArray)"); arr.AddValue(dict); } break; } - case JsonToken.EndArray: - { - var array = serializer.Deserialize(reader) - ?? throw new CouchbaseLiteException(C4ErrorCode.UnexpectedError, "Error deserializing array in ReadJson (IArray)"); + case JsonTokenType.StartArray: + var array = JsonSerializer.Deserialize(ref reader) ?? + throw new CouchbaseLiteException(C4ErrorCode.UnexpectedError, + "Error deserializing array in ReadJson (IArray)"); arr.AddValue(array); break; - } - case JsonToken.None: - case JsonToken.StartObject: - case JsonToken.StartArray: - case JsonToken.StartConstructor: - case JsonToken.PropertyName: - case JsonToken.Comment: - case JsonToken.Raw: - case JsonToken.Integer: - case JsonToken.Float: - case JsonToken.String: - case JsonToken.Boolean: - case JsonToken.Null: - case JsonToken.Undefined: - case JsonToken.EndConstructor: - case JsonToken.Date: - case JsonToken.Bytes: + case JsonTokenType.String: + arr.AddValue(reader.GetString()); + break; + case JsonTokenType.Number: + if (reader.TryGetInt64(out var longValue)) { + arr.AddValue(longValue); + } else { + arr.AddValue(reader.GetDouble()); + } + break; + case JsonTokenType.True: + case JsonTokenType.False: + arr.AddValue(reader.GetBoolean()); + break; + case JsonTokenType.Null: + arr.AddValue(null); + break; + case JsonTokenType.None: + case JsonTokenType.EndObject: + case JsonTokenType.EndArray: + case JsonTokenType.PropertyName: + case JsonTokenType.Comment: default: - arr.AddValue(reader.Value); break; } } @@ -238,5 +234,13 @@ public override object ReadJson(JsonReader reader, Type objectType, object? exis return arr; } - public override bool CanConvert(Type objectType) => typeof(IArray).IsAssignableFrom(objectType); + public override void Write(Utf8JsonWriter writer, IArray value, JsonSerializerOptions options) + { + writer.WriteStartArray(); + foreach (var item in value) { + JsonSerializer.Serialize(writer, item, options); + } + + writer.WriteEndArray(); + } } \ No newline at end of file diff --git a/src/Couchbase.Lite.Shared/API/Document/Blob.cs b/src/Couchbase.Lite.Shared/API/Document/Blob.cs index 8fcd42f44..c8e4adf55 100644 --- a/src/Couchbase.Lite.Shared/API/Document/Blob.cs +++ b/src/Couchbase.Lite.Shared/API/Document/Blob.cs @@ -21,13 +21,14 @@ using System.Collections.ObjectModel; using System.IO; using System.Runtime.InteropServices; +using System.Text.Json; +using System.Text.Json.Serialization; using Couchbase.Lite.Internal.Doc; using Couchbase.Lite.Internal.Logging; using Couchbase.Lite.Util; using LiteCore; using LiteCore.Interop; -using Newtonsoft.Json; namespace Couchbase.Lite; @@ -56,7 +57,7 @@ public sealed unsafe class Blob : IJSON /// /// Gets the metadata type of this object (hardcoded to ) /// - [JsonProperty("@type")] + [JsonPropertyName("@type")] #pragma warning disable CA1822 public string Type => Constants.ObjectTypeBlob; #pragma warning restore CA1822 @@ -153,19 +154,19 @@ public Stream? ContentStream /// /// Gets the content type of the blob /// - [JsonProperty("content_type")] + [JsonPropertyName("content_type")] public string? ContentType { get; } /// /// Gets the digest of the blob, once it is saved /// - [JsonProperty("digest")] + [JsonPropertyName("digest")] public string? Digest { get; internal set; } /// /// Gets the length of the data that the blob contains /// - [JsonProperty("length")] + [JsonPropertyName("length")] public int Length { get; private set; } /// @@ -378,7 +379,7 @@ internal void Install(Database db) public string ToJSON() => Digest == null ? throw new InvalidOperationException(CouchbaseLiteErrorMessage.MissingDigestDueToBlobIsNotSavedToDB) - : JsonConvert.SerializeObject(JsonRepresentation); + : JsonSerializer.Serialize(JsonRepresentation); private void SetupProperties(IDictionary properties) { diff --git a/src/Couchbase.Lite.Shared/API/Document/DictionaryObject.cs b/src/Couchbase.Lite.Shared/API/Document/DictionaryObject.cs index 940aae191..68dec9d4e 100644 --- a/src/Couchbase.Lite.Shared/API/Document/DictionaryObject.cs +++ b/src/Couchbase.Lite.Shared/API/Document/DictionaryObject.cs @@ -21,54 +21,48 @@ using System.Collections.Generic; using System.IO; using System.Linq; +using System.Text.Json; +using System.Text.Json.Serialization; using Couchbase.Lite.Internal.Doc; using Couchbase.Lite.Internal.Serialization; using Couchbase.Lite.Support; -using LiteCore.Interop; -using Newtonsoft.Json; namespace Couchbase.Lite; [System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage] -internal sealed class IDictionaryObjectConverter : JsonConverter +internal sealed class IDictionaryObjectConverter : JsonConverter { - public override void WriteJson(JsonWriter writer, object? value, JsonSerializer serializer) - { - var dict = value as IDictionaryObject ?? throw new CouchbaseLiteException(C4ErrorCode.UnexpectedError, - "Invalid input received in WriteJson (not IDictionaryObject)"); - writer.WriteStartObject(); - foreach (var pair in dict) { - writer.WritePropertyName(pair.Key); - serializer.Serialize(writer, pair.Value); - } - writer.WriteEndObject(); - } - - public override object ReadJson(JsonReader reader, Type objectType, object? existingValue, JsonSerializer serializer) + public override IDictionaryObject Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) { var dict = new MutableDictionaryObject(); - if (reader.TokenType == JsonToken.StartObject) { + if (reader.TokenType == JsonTokenType.StartObject) { reader.Read(); } - while (reader.TokenType != JsonToken.EndObject && reader.Read()) { - var key = reader.Value as string; + while (reader.TokenType != JsonTokenType.EndObject && reader.Read()) { + var key = reader.GetString(); if (key == null) { throw new InvalidDataException(CouchbaseLiteErrorMessage.InvalidValueToBeDeserialized); } reader.Read(); - var value = reader.Value; + var value = JsonSerializer.Deserialize(ref reader, options); dict.SetValue(key, value); } return dict; } - public override bool CanConvert(Type objectType) + public override void Write(Utf8JsonWriter writer, IDictionaryObject value, JsonSerializerOptions options) { - return typeof(IDictionaryObject).IsAssignableFrom(objectType); + writer.WriteStartObject(); + foreach (var pair in value) { + writer.WritePropertyName(pair.Key); + JsonSerializer.Serialize(writer, pair.Value); + } + + writer.WriteEndObject(); } } diff --git a/src/Couchbase.Lite.Shared/API/Document/Document.cs b/src/Couchbase.Lite.Shared/API/Document/Document.cs index 48108acc6..84c84d20c 100644 --- a/src/Couchbase.Lite.Shared/API/Document/Document.cs +++ b/src/Couchbase.Lite.Shared/API/Document/Document.cs @@ -246,19 +246,6 @@ public virtual MutableDocument ToMutable() => ? throw new InvalidOperationException(CouchbaseLiteErrorMessage.NoDocEditInReplicationFilter) : new MutableDocument(this); -#if CBL_LINQ - public T ToModel() where T : class, Linq.IDocumentModel, new() - { - var serializer = Newtonsoft.Json.JsonSerializer.CreateDefault(); - var flValue = NativeRaw.FLValue_FromTrustedData((FLSlice) c4Doc.RawDoc->selectedRev.body); - using (var reader = new Internal.Serialization.JsonFLValueReader(flValue, Database.SharedStrings)) { - var retVal = serializer.Deserialize(reader); - retVal.Document = this; - return retVal; - } - } -#endif - internal virtual FLSliceResult Encode() { _disposalWatchdog.CheckDisposed(); diff --git a/src/Couchbase.Lite.Shared/API/Document/IArray.cs b/src/Couchbase.Lite.Shared/API/Document/IArray.cs index 5c5a21aa0..6ed91be65 100644 --- a/src/Couchbase.Lite.Shared/API/Document/IArray.cs +++ b/src/Couchbase.Lite.Shared/API/Document/IArray.cs @@ -19,14 +19,11 @@ using System; using System.Collections.Generic; -using Newtonsoft.Json; - namespace Couchbase.Lite; /// /// An interface representing a read-only linear collection of objects /// -[JsonConverter(typeof(IArrayConverter))] public interface IArray : IArrayFragment, IEnumerable { /// diff --git a/src/Couchbase.Lite.Shared/API/Document/IDictionaryObject.cs b/src/Couchbase.Lite.Shared/API/Document/IDictionaryObject.cs index 2a85431cb..d36a76464 100644 --- a/src/Couchbase.Lite.Shared/API/Document/IDictionaryObject.cs +++ b/src/Couchbase.Lite.Shared/API/Document/IDictionaryObject.cs @@ -18,14 +18,11 @@ using System; using System.Collections.Generic; -using Newtonsoft.Json; - namespace Couchbase.Lite; /// /// An interface representing a readonly key-value collection with type-safe accessors /// -[JsonConverter(typeof(IDictionaryObjectConverter))] public interface IDictionaryObject : IDictionaryFragment, IEnumerable> { /// diff --git a/src/Couchbase.Lite.Shared/API/Document/MutableDocument.cs b/src/Couchbase.Lite.Shared/API/Document/MutableDocument.cs index da51b1b3a..3006c9976 100644 --- a/src/Couchbase.Lite.Shared/API/Document/MutableDocument.cs +++ b/src/Couchbase.Lite.Shared/API/Document/MutableDocument.cs @@ -35,14 +35,6 @@ namespace Couchbase.Lite; /// public sealed class MutableDocument : Document, IMutableDictionary { - #if CBL_LINQ - private Linq.IDocumentModel _model; - #endif - - #if CBL_LINQ - internal override bool IsEmpty => _model == null && base.IsEmpty; - #endif - private protected override bool IsMutable => true; private IMutableDictionary? Dict => _dict as IMutableDictionary; @@ -121,25 +113,6 @@ private MutableDocument(MutableDocument other) _dict = dict; } - #if CBL_LINQ - internal void SetFromModel(Linq.IDocumentModel model) - { - _model = model; - } - #endif - - #if CBL_LINQ - private FLSliceResult EncodeModel(FLEncoder* encoder) - { - var serializer = JsonSerializer.CreateDefault(); - using (var writer = new Internal.Serialization.JsonFLValueWriter(c4Db)) { - serializer.Serialize(writer, _model); - writer.Flush(); - return writer.Result; - } - } - #endif - private static object? MutableCopy(object? original) { return original switch diff --git a/src/Couchbase.Lite.Shared/API/Log/ConsoleLogSink.cs b/src/Couchbase.Lite.Shared/API/Log/ConsoleLogSink.cs index 8cfa950ba..bd999e351 100644 --- a/src/Couchbase.Lite.Shared/API/Log/ConsoleLogSink.cs +++ b/src/Couchbase.Lite.Shared/API/Log/ConsoleLogSink.cs @@ -22,6 +22,7 @@ using System.Diagnostics; using System.Diagnostics.CodeAnalysis; using System.Threading; +using Microsoft.Extensions.DependencyInjection; namespace Couchbase.Lite.Logging; @@ -36,7 +37,7 @@ namespace Couchbase.Lite.Logging; /// The log level to emit (see ) public sealed class ConsoleLogSink(LogLevel level) : BaseLogSink(level) { - private readonly IConsoleLogWriter _logWriter = Service.GetRequiredInstance(); + private readonly IConsoleLogWriter _logWriter = Service.Provider.GetRequiredService(); /// /// Gets the domains to include for logging (useful for reducing noise when diff --git a/src/Couchbase.Lite.Shared/API/Query/Parameters.cs b/src/Couchbase.Lite.Shared/API/Query/Parameters.cs index 18feadfab..e85163383 100644 --- a/src/Couchbase.Lite.Shared/API/Query/Parameters.cs +++ b/src/Couchbase.Lite.Shared/API/Query/Parameters.cs @@ -18,7 +18,7 @@ using System; using System.Collections.Generic; - +using System.Text.Json; using Couchbase.Lite.Internal.Logging; using Couchbase.Lite.Internal.Query; using Couchbase.Lite.Support; @@ -26,8 +26,6 @@ using LiteCore.Interop; -using Newtonsoft.Json; - namespace Couchbase.Lite.Query; /// @@ -226,5 +224,5 @@ internal Parameters Freeze() internal FLSliceResult FLEncode() => _params.FLEncode(); /// - public override string ToString() => JsonConvert.SerializeObject(_params); + public override string ToString() => JsonSerializer.Serialize(_params); } \ No newline at end of file diff --git a/src/Couchbase.Lite.Shared/API/Query/Result.cs b/src/Couchbase.Lite.Shared/API/Query/Result.cs index f9c34e3b2..2ff67c7d7 100644 --- a/src/Couchbase.Lite.Shared/API/Query/Result.cs +++ b/src/Couchbase.Lite.Shared/API/Query/Result.cs @@ -20,7 +20,7 @@ using System.Collections; using System.Collections.Generic; using System.Linq; - +using System.Text.Json; using Couchbase.Lite.Internal.Doc; using Couchbase.Lite.Internal.Logging; using Couchbase.Lite.Internal.Query; @@ -28,7 +28,6 @@ using Couchbase.Lite.Util; using LiteCore.Interop; -using Newtonsoft.Json; namespace Couchbase.Lite.Query; @@ -302,5 +301,5 @@ public long GetLong(string key) } /// - public string ToJSON() => JsonConvert.SerializeObject(ToDictionary()); + public string ToJSON() => JsonSerializer.Serialize(ToDictionary()); } \ No newline at end of file diff --git a/src/Couchbase.Lite.Shared/API/Sync/Replicator.cs b/src/Couchbase.Lite.Shared/API/Sync/Replicator.cs index 003f10920..303ff5094 100644 --- a/src/Couchbase.Lite.Shared/API/Sync/Replicator.cs +++ b/src/Couchbase.Lite.Shared/API/Sync/Replicator.cs @@ -39,6 +39,7 @@ using LiteCore.Util; using Dispatch; +using Microsoft.Extensions.DependencyInjection; #if !NET8_0_OR_GREATER #pragma warning disable CS8602 // Dereference of a possibly null reference. @@ -776,7 +777,7 @@ private void StartReachabilityObserver() return; } - _reachability = Service.GetInstance() ?? new Reachability(); + _reachability = Service.Provider.GetService() ?? new Reachability(); _reachability.StatusChanged += ReachabilityChanged; _reachability.Url = remoteUrl; _reachability.Start(); diff --git a/src/Couchbase.Lite.Shared/Couchbase.Lite.Shared.projitems b/src/Couchbase.Lite.Shared/Couchbase.Lite.Shared.projitems index 02dc06362..7f4bafd37 100644 --- a/src/Couchbase.Lite.Shared/Couchbase.Lite.Shared.projitems +++ b/src/Couchbase.Lite.Shared/Couchbase.Lite.Shared.projitems @@ -132,22 +132,11 @@ - - - - - - - - - - - @@ -174,8 +163,6 @@ - - diff --git a/src/Couchbase.Lite.Shared/Document/DataOps.cs b/src/Couchbase.Lite.Shared/Document/DataOps.cs index 737d1fd85..ec0f6a745 100644 --- a/src/Couchbase.Lite.Shared/Document/DataOps.cs +++ b/src/Couchbase.Lite.Shared/Document/DataOps.cs @@ -19,24 +19,89 @@ using System.Collections; using System.Collections.Generic; using System.Globalization; +using System.Text.Json; +using System.Text.Json.Serialization; using Couchbase.Lite.Internal.Serialization; using LiteCore.Interop; -using Newtonsoft.Json; -using Newtonsoft.Json.Linq; namespace Couchbase.Lite.Internal.Doc; +internal sealed class CouchbaseConverter : JsonConverter +{ + public override object? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + { + return reader.TokenType switch + { + JsonTokenType.True => true, + JsonTokenType.False => false, + JsonTokenType.Number => ReadNumber(ref reader), + JsonTokenType.String => reader.GetString(), + JsonTokenType.StartObject => ReadObject(ref reader), + JsonTokenType.StartArray => ReadArray(ref reader), + JsonTokenType.Null => null, + _ => throw new JsonException($"Unexpected token parsing JSON. Token: {reader.TokenType}") + }; + } + + public override void Write(Utf8JsonWriter writer, object value, JsonSerializerOptions options) + { + JsonSerializer.Serialize(writer, value, options); + } + + private object ReadNumber(ref Utf8JsonReader reader) + { + if (reader.TryGetInt64(out var l)) + return l; + + return reader.GetDouble(); + } + + private Dictionary ReadObject(ref Utf8JsonReader reader) + { + var dictionary = new Dictionary(); + + while (reader.Read()) { + if (reader.TokenType == JsonTokenType.EndObject) + break; + + if (reader.TokenType != JsonTokenType.PropertyName) + throw new JsonException("Expected property name"); + + var propertyName = reader.GetString()!; + reader.Read(); + dictionary[propertyName] = Read(ref reader, typeof(object), null!)!; + } + + return dictionary; + } + + private List ReadArray(ref Utf8JsonReader reader) + { + var list = new List(); + while (reader.Read()) { + if (reader.TokenType == JsonTokenType.EndArray) + break; + + list.Add(Read(ref reader, typeof(object), null!)!); + } + + return list; + } +} + internal static class DataOps { + internal static readonly JsonSerializerOptions SerializerOptions = new() + { + Converters = { new CouchbaseConverter(), new IArrayConverter(), new IDictionaryObjectConverter() }, + PreferredObjectCreationHandling = JsonObjectCreationHandling.Replace + }; + internal static T? ParseTo(string json) { T? retVal; try { - var settings = new JsonSerializerSettings { - DateParseHandling = DateParseHandling.DateTimeOffset, - TypeNameHandling = TypeNameHandling.All - }; - retVal = JsonConvert.DeserializeObject(json, settings); + retVal = JsonSerializer.Deserialize(json, SerializerOptions); } catch { throw new CouchbaseLiteException(C4ErrorCode.InvalidParameter, CouchbaseLiteErrorMessage.InvalidJSON); } @@ -120,19 +185,24 @@ internal static long ConvertToLong(object? value) return rodic.ToMutable(); case ArrayObject roarr and not MutableArrayObject: return roarr.ToMutable(); - case JObject jobj: - var jobjDict = jobj.ToObject>(); - - //The dictionary may contain the json dict represents Blob. Developer should be able to retrieve Blob object using the Database.GetBlob(dict). - return ConvertDictionary(jobjDict!); - case JArray jarr: - return ConvertList(jarr.ToObject()!); - case JToken jToken: - return jToken.Type switch - { - JTokenType.Date => (DateTimeOffset)jToken, - _ => jToken.ToObject() - }; + case JsonElement jobj: + switch (jobj.ValueKind) { + case JsonValueKind.Array: + return ConvertList(jobj.Deserialize(SerializerOptions)!); + case JsonValueKind.Object: + return ConvertDictionary(jobj.Deserialize>(SerializerOptions)!); + case JsonValueKind.Number: + return jobj.TryGetInt64(out var l) ? l : jobj.GetDouble(); + case JsonValueKind.String: + return jobj.GetString(); + case JsonValueKind.True: + case JsonValueKind.False: + return jobj.GetBoolean(); + case JsonValueKind.Null: + return null; + default: + throw new ArgumentException("Invalid JsonElement type: " + jobj.ValueKind); + } case IDictionary dict: return ConvertDictionary(dict); case IList list: diff --git a/src/Couchbase.Lite.Shared/Linq/IDocumentModel.cs b/src/Couchbase.Lite.Shared/Linq/IDocumentModel.cs deleted file mode 100644 index c3c065fab..000000000 --- a/src/Couchbase.Lite.Shared/Linq/IDocumentModel.cs +++ /dev/null @@ -1,29 +0,0 @@ -// -// IDocumentModel.cs -// -// Copyright (c) 2017 Couchbase, Inc All rights reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// -#if CBL_LINQ -using Newtonsoft.Json; - -namespace Couchbase.Lite.Linq -{ - public interface IDocumentModel - { - [JsonIgnore] - Document Document { get; set; } - } -} -#endif \ No newline at end of file diff --git a/src/Couchbase.Lite.Shared/Linq/ISelectResultContainer.cs b/src/Couchbase.Lite.Shared/Linq/ISelectResultContainer.cs deleted file mode 100644 index 5c0f8794b..000000000 --- a/src/Couchbase.Lite.Shared/Linq/ISelectResultContainer.cs +++ /dev/null @@ -1,113 +0,0 @@ -// -// ISelectResultContainer.cs -// -// Copyright (c) 2017 Couchbase, Inc All rights reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// -#if CBL_LINQ -using Couchbase.Lite.Internal.Serialization; -using LiteCore.Interop; -using Newtonsoft.Json; -using System; -using System.Collections; -using System.Collections.Generic; -using System.Reflection; - -namespace Couchbase.Lite.Linq -{ - internal interface ISelectResultContainer - { - #region Properties - - object Results { get; } - - #endregion - - #region Public Methods - - void Populate(FLArrayIterator iterator, SharedStringCache sharedStrings); - - #endregion - } - - internal sealed class SelectResultListContainer : ISelectResultContainer - { - #region Variables - - private readonly Type _elementType; - - #endregion - - #region Properties - - public object Results { get; private set; } - - #endregion - - #region Constructors - - public SelectResultListContainer(Type listType) - { - _elementType = listType; - } - - #endregion - - #region ISelectResultContainer - - public unsafe void Populate(FLArrayIterator iterator, SharedStringCache sharedStrings) - { - var i = iterator; - var serializer = JsonSerializer.CreateDefault(); - var count = Native.FLArrayIterator_GetCount(&i); - Results = Activator.CreateInstance(_elementType.MakeArrayType(), (int) count); - for (var index = 0U; index < count; index++) { - using (var reader = new JsonFLValueReader(Native.FLArrayIterator_GetValueAt(&i, index), sharedStrings)) { - (Results as IList)[(int)index] = serializer.Deserialize(reader); - } - } - } - - #endregion - } - - internal sealed class SelectResultAnonymousContainer : ISelectResultContainer - { - private readonly ConstructorInfo _constructor; - - public object Results { get; private set; } - - public SelectResultAnonymousContainer(ConstructorInfo constructor) - { - _constructor = constructor; - - } - - public unsafe void Populate(FLArrayIterator iterator, SharedStringCache sharedStrings) - { - var iter = iterator; - var serializer = JsonSerializer.CreateDefault(); - var parameters = new List(); - for (var i = 0U; i < Native.FLArrayIterator_GetCount(&iter); i++) { - using (var reader = new JsonFLValueReader(Native.FLArrayIterator_GetValueAt(&iter, i), sharedStrings)) { - var nextItem = serializer.Deserialize(reader, _constructor.GetParameters()[(int) i].ParameterType); - parameters.Add(nextItem); - } - } - - Results = _constructor.Invoke(parameters.ToArray()); - } - } -} -#endif \ No newline at end of file diff --git a/src/Couchbase.Lite.Shared/Linq/LinqExtensionMethods.cs b/src/Couchbase.Lite.Shared/Linq/LinqExtensionMethods.cs deleted file mode 100644 index c7b54f3f1..000000000 --- a/src/Couchbase.Lite.Shared/Linq/LinqExtensionMethods.cs +++ /dev/null @@ -1,164 +0,0 @@ -// -// LinqExtensionMethods.cs -// -// Copyright (c) 2017 Couchbase, Inc All rights reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// -#if CBL_LINQ -using System; -using System.Collections.Generic; -using System.Diagnostics.CodeAnalysis; -using System.Linq; -using System.Runtime.CompilerServices; -using System.Text.RegularExpressions; - -namespace Couchbase.Lite.Linq -{ - /// - /// A collection of methods for use with the new LINQ model - /// - internal static class LinqExtensionMethods - { - #region Public Methods - - /// - /// Returns whether or not the collection has at least one item AND at least one item - /// matching the given pattern - /// - /// The type of collection items to operate on - /// The collection to operate on (implicit) - /// The predicate to use - /// true if the collection has at least one element and at least one element matching the - /// given pattern, false otherwise - [SuppressMessage("ReSharper", "PossibleMultipleEnumeration", Justification = "Any only needs to check the first element")] - public static bool AnyAndEvery(this IEnumerable collection, Func predicate) - { - return collection.Any() && collection.All(predicate); - } - - /// - /// Returns if a given is between a given min and max - /// - /// The number to test (implicit) - /// The lower bound - /// The upper bound - /// true if l is between min and max, false otherwise - public static bool Between(this long l, long min, long max) - { - return l >= min && l <= max; - } - - /// - /// Returns if a given is between a given min and max - /// - /// The number to test (implicit) - /// The lower bound - /// The upper bound - /// true if i is between min and max, false otherwise - public static bool Between(this int i, int min, int max) - { - return Between((long)i, min, max); - } - - /// - /// Returns if a given is between a given min and max - /// - /// The number to test (implicit) - /// The lower bound - /// The upper bound - /// true if s is between min and max, false otherwise - public static bool Between(this short s, short min, short max) - { - return Between((long)s, min, max); - } - - /// - /// Returns if a given is between a given min and max - /// - /// The number to test (implicit) - /// The lower bound - /// The upper bound - /// true if b is between min and max, false otherwise - public static bool Between(this sbyte b, sbyte min, sbyte max) - { - return Between((long)b, min, max); - } - - /// - /// Returns if a given is between a given min and max - /// - /// The number to test (implicit) - /// The lower bound - /// The upper bound - /// true if u is between min and max, false otherwise - public static bool Between(this ulong u, ulong min, ulong max) - { - return u >= min && u <= max; - } - - /// - /// Returns if a given is between a given min and max - /// - /// The number to test (implicit) - /// The lower bound - /// The upper bound - /// true if u is between min and max, false otherwise - public static bool Between(this uint u, uint min, uint max) - { - return Between((ulong)u, min, max); - } - - /// - /// Returns if a given is between a given min and max - /// - /// The number to test (implicit) - /// The lower bound - /// The upper bound - /// true if u is between min and max, false otherwise - public static bool Between(this ushort u, ushort min, ushort max) - { - return Between((ulong)u, min, max); - } - - /// - /// Returns if a given is between a given min and max - /// - /// The number to test (implicit) - /// The lower bound - /// The upper bound - /// true if u is between min and max, false otherwise - public static bool Between(this byte b, byte min, byte max) - { - return Between((ulong)b, min, max); - } - - public static bool Like(this string item, string pattern) - { - return false; - } - - public static string Id(this object obj) - { - return String.Empty; - } - - public static ulong Sequence(this object obj) - { - return 0UL; - } - - #endregion - } -} -#endif \ No newline at end of file diff --git a/src/Couchbase.Lite.Shared/Linq/LiteCoreExpressionVisitor.cs b/src/Couchbase.Lite.Shared/Linq/LiteCoreExpressionVisitor.cs deleted file mode 100644 index 4491c1137..000000000 --- a/src/Couchbase.Lite.Shared/Linq/LiteCoreExpressionVisitor.cs +++ /dev/null @@ -1,469 +0,0 @@ -// -// LiteCoreExpressionVisitor.cs -// -// Copyright (c) 2017 Couchbase, Inc All rights reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// -#if CBL_LINQ -using System; -using System.Collections; -using System.Collections.Generic; -using System.Diagnostics.CodeAnalysis; -using System.Linq; -using System.Linq.Expressions; -using System.Reflection; -using System.Text; -using System.Text.RegularExpressions; - -using Newtonsoft.Json; - -using Remotion.Linq.Clauses; -using Remotion.Linq.Clauses.Expressions; -using Remotion.Linq.Clauses.ResultOperators; - -namespace Couchbase.Lite.Internal.Linq -{ - internal abstract class LiteCoreExpressionVisitor : NotSupportedExpressionVisitor - { - #region Constants - - private static readonly IDictionary _ExpressionTypeMap = - new Dictionary - { - [ExpressionType.LessThan] = "<", - [ExpressionType.LessThanOrEqual] = "<=", - [ExpressionType.GreaterThan] = ">", - [ExpressionType.GreaterThanOrEqual] = ">=", - [ExpressionType.Equal] = "=", - [ExpressionType.NotEqual] = "!=", - [ExpressionType.Multiply] = "*", - [ExpressionType.Add] = "+", - [ExpressionType.Subtract] = "-", - [ExpressionType.Divide] = "/", - [ExpressionType.Modulo] = "%", - [ExpressionType.AndAlso] = "AND", - [ExpressionType.OrElse] = "OR" - }; - - #endregion - - #region Variables - - private readonly IDictionary> _methodMap; - protected readonly IList _query = new List(); - private IList _currentExpression; - - private Mode _currentMode; - - #endregion - - private IList CurrentExpression - { - get { - if (_currentExpression == null) { - _currentExpression = new List(); - _query.Add(_currentExpression); - } - - return _currentExpression; - } - } - - protected bool IncludeDbNames { get; set; } - - #region Constructors - - protected LiteCoreExpressionVisitor() - { - _methodMap = new Dictionary> - { - ["AnyAndEvery"] = HandleAnyAndEvery, - ["IsMatch"] = HandleIsMatch, - ["Contains"] = HandleContains, - ["Between"] = HandleBetween, - ["Id"] = HandleId, - ["Sequence"] = HandleSequence, - ["Like"] = HandleLike, - ["FullTextMatches"] = HandleFullTextMatches, - ["FullTextRank"] = HandleFullTextRank, - ["Abs"] = HandleMath, - ["Acos"] = HandleMath, - ["Asin"] = HandleMath, - ["Atan"] = HandleMath, - ["Ceiling"] = HandleMath - }; - } - - #endregion - - #region Private Methods - - private void AppendOperand(BinaryExpression expression) - { - if (_ExpressionTypeMap.ContainsKey(expression.NodeType)) { - if (_currentExpression == null) { - _currentExpression = new List(); - _query.Add(_currentExpression); - } else { - var next = new List(); - _currentExpression.Add(next); - _currentExpression = next; - } - - _currentExpression.Add(_ExpressionTypeMap[expression.NodeType]); - return; - } - - base.VisitBinary(expression); - } - - private void HandleAllAnyEvery(string keyword, Expression part1, Expression part2) - { - var overallExpression = new List { keyword, "X" }; - _currentExpression = overallExpression; - _query.Add(_currentExpression); - Visit(part1); - _currentExpression = new List(); - _currentMode = Mode.AllOperator; - Visit(part2); - overallExpression.Add(_currentExpression); - } - - private void HandleAnyAndEvery(MethodCallExpression expression) - { - HandleAllAnyEvery("ANY AND EVERY", expression.Arguments[0], expression.Arguments[1]); - } - - private void HandleBetween(MethodCallExpression expression) - { - _currentExpression = new List { "BETWEEN" }; - Visit(expression.Arguments[0]); - Visit(expression.Arguments[1]); - Visit(expression.Arguments[2]); - _query.Add(_currentExpression); - } - - private void HandleContains(MethodCallExpression expression) - { - if (expression.Object.Type != typeof(string)) { - base.VisitMethodCall(expression); - return; - } - - _currentExpression = new List { "CONTAINS()" }; - Visit(expression.Object); - Visit(expression.Arguments[0]); - _query.Add(_currentExpression); - } - - private void HandleId(MethodCallExpression expression) - { - CurrentExpression.Add(new[] { "._id" }); - } - - private void HandleIsMatch(MethodCallExpression expression) - { - _currentExpression = new List { "MATCH()" }; - - Visit(expression.Arguments[0]); - Visit(expression.Arguments[1]); - _query.Add(_currentExpression); - } - - private void HandleFullTextRank(MethodCallExpression expression) - { - _currentExpression = new List { "RANK()" }; - - Visit(expression.Arguments[0]); - Visit(expression.Arguments[1]); - _query.Add(_currentExpression); - } - - private void HandleMath(MethodCallExpression expression) - { - _currentExpression = new List { $"{expression.Method.Name.ToUpperInvariant()}()" }; - Visit(expression.Arguments[0]); - _query.Add(_currentExpression); - } - - private void HandleFullTextMatches(MethodCallExpression expression) - { - _currentExpression = new List { "REGEXP_LIKE()" }; - } - - private void HandleLike(MethodCallExpression expression) - { - _currentExpression = new List { "LIKE" }; - Visit(expression.Arguments[0]); - Visit(expression.Arguments[1]); - _query.Add(_currentExpression); - } - - private void HandleSequence(MethodCallExpression expression) - { - CurrentExpression.Add(new[] { "._sequence" }); - } - - private bool TryHandleAll(SubQueryExpression subquery, AllResultOperator ro) => TryHandleAllAny(subquery, ro.Predicate, "EVERY"); - - private bool TryHandleAny(SubQueryExpression subquery, AnyResultOperator ro) => TryHandleAllAny(subquery, (subquery.QueryModel.BodyClauses.FirstOrDefault() as WhereClause)?.Predicate, "ANY"); - - private bool TryHandleAllAny(SubQueryExpression subquery, Expression predicate, string keyword) - { - var last = _currentMode; - var part1 = subquery.QueryModel.MainFromClause.FromExpression; - if (predicate == null) { - return false; - } - - - HandleAllAnyEvery(keyword, part1, predicate); - _currentMode = last; - return true; - } - - private void HandleFirst(SubQueryExpression subquery, FirstResultOperator ro) => - HandleFirstLast(subquery, "[0]"); - - private void HandleLast(SubQueryExpression subquery, LastResultOperator ro) => - HandleFirstLast(subquery, "[-1]"); - - [SuppressMessage("ReSharper", "PossibleNullReferenceException", Justification = - "These types are all defined by the type of it they enter")] - private void HandleFirstLast(SubQueryExpression subquery, string expressionAddition) - { - var from = subquery.QueryModel.MainFromClause.FromExpression; - _currentExpression = _query.Last() as IList; - Visit(from); - var overallExpression = new StringBuilder((_currentExpression.Last() as IList)[0]); - overallExpression.Append(expressionAddition); - _currentExpression.RemoveAt(_currentExpression.Count - 1); - _currentExpression.Add(new[] { overallExpression.ToString() }); - } - - private void HandleSum(SubQueryExpression subquery, SumResultOperator ro) => - HandleArrayFunc(subquery, "ARRAY_SUM()"); - - private void HandleCount(SubQueryExpression subquery, CountResultOperator ro) => - HandleArrayFunc(subquery, "ARRAY_COUNT()"); - - private void HandleMax(SubQueryExpression subquery, MaxResultOperator ro) => - HandleArrayFunc(subquery, "ARRAY_MAX()"); - - private bool TryHandleContains(SubQueryExpression subquery, ContainsResultOperator ro) - { - switch (subquery.QueryModel.MainFromClause.FromExpression) { - case ConstantExpression expr: - if (!(expr.Value is IList list)) { - return false; - } - - CurrentExpression.Add("IN"); - Visit(ro.Item); - var opList = new List { "[]" }; - opList.AddRange(list.Cast()); - CurrentExpression.Add(opList); - return true; - case MemberExpression expr: - var last = _currentExpression; - CurrentExpression.Add("ARRAY_CONTAINS()"); - Visit(expr); - Visit(ro.Item); - _currentExpression = last; - return true; - } - - return false; - } - - private void HandleArrayFunc(SubQueryExpression subquery, string function) - { - var overallExpression = _query.LastOrDefault() as IList ?? _query; - _currentExpression = new List { function }; - var from = subquery.QueryModel.MainFromClause.FromExpression; - Visit(from); - overallExpression.Add(_currentExpression); - _currentExpression = overallExpression; - } - - #endregion - - - #region Overrides - - protected override Expression VisitBinary(BinaryExpression expression) - { - AppendOperand(expression); - var last = _currentExpression; - Visit(expression.Left); - _currentExpression = last; - Visit(expression.Right); - _currentExpression = last; - - return expression; - } - - protected override Expression VisitConstant(ConstantExpression node) - { - _currentExpression.Add(node.Value); - return node; - } - - protected override Expression VisitExtension(Expression node) - { - if (!(node is SubQueryExpression subquery)) { - return base.VisitExtension(node); - } - - switch (subquery.QueryModel.ResultOperators.FirstOrDefault()) { - case null: - return base.VisitExtension(node); - case AllResultOperator ro: - { - if (TryHandleAll(subquery, ro)) { - return node; - } - - break; - } - case AnyResultOperator ro: - { - if (TryHandleAny(subquery, ro)) { - return node; - } - - break; - } - case FirstResultOperator ro: - { - HandleFirst(subquery, ro); - return node; - } - case LastResultOperator ro: - { - HandleLast(subquery, ro); - return node; - } - case SumResultOperator ro: - { - HandleSum(subquery, ro); - return node; - } - case CountResultOperator ro: - { - HandleCount(subquery, ro); - return node; - } - case MaxResultOperator ro: - { - HandleMax(subquery, ro); - return node; - } - case ContainsResultOperator ro: - { - if (TryHandleContains(subquery, ro)) { - return node; - } - - break; - } - } - - return base.VisitExtension(node); - } - - protected override Expression VisitInvocation(InvocationExpression node) - { - Visit(node.Expression); - return node; - } - - protected override Expression VisitLambda(Expression node) - { - Visit(node.Body); - return node; - } - - [SuppressMessage("ReSharper", "PossibleNullReferenceException", Justification = - "These types are all defined by the type of it they enter")] - protected override Expression VisitMember(MemberExpression node) - { - var sb = new StringBuilder(); - - var currentNode = (Expression) node; - while (currentNode.NodeType == ExpressionType.MemberAccess || currentNode.NodeType == ExpressionType.Call) { - if (currentNode.NodeType == ExpressionType.Call) { - var ce = (currentNode as MethodCallExpression); - if (ce.Method.Name != "get_Item") { - return base.VisitMember(node); - } - - var me = ce.Object as MemberExpression; - var index = ((ConstantExpression) ce.Arguments[0]).Value; - sb.Insert(0, $".{me.Member.Name}[{index}]"); - currentNode = me.Expression; - } else { - var me = (currentNode as MemberExpression); - var mappingProperty = - me.Member.GetCustomAttribute(typeof(JsonPropertyAttribute)) as JsonPropertyAttribute; - var name = mappingProperty?.PropertyName ?? me.Member.Name; - sb.Insert(0, $".{name}"); - currentNode = me.Expression; - } - } - - if (IncludeDbNames && currentNode is QuerySourceReferenceExpression sourceRef) { - sb.Insert(0, $".{sourceRef.ReferencedQuerySource.ItemName}"); - } - - if (_currentMode == Mode.AllOperator) { - sb.Remove(0, 1); - CurrentExpression.Add(new[] { "?X", sb.ToString() }); - } else { - CurrentExpression.Add(new[] { sb.ToString() }); - } - - return node; - } - - protected override Expression VisitMethodCall(MethodCallExpression node) - { - if (!_methodMap.TryGetValue(node.Method.Name, out var handler)) { - return base.VisitMethodCall(node); - } - - handler(node); - return node; - } - - protected override Expression VisitUnary(UnaryExpression node) - { - Visit(node.Operand); - return node; - } - - #endregion - - #region Nested - - private enum Mode - { - Normal, - AllOperator - } - - #endregion - } -} -#endif \ No newline at end of file diff --git a/src/Couchbase.Lite.Shared/Linq/LiteCoreOrderingExpressionVisitor.cs b/src/Couchbase.Lite.Shared/Linq/LiteCoreOrderingExpressionVisitor.cs deleted file mode 100644 index ace3e9d1d..000000000 --- a/src/Couchbase.Lite.Shared/Linq/LiteCoreOrderingExpressionVisitor.cs +++ /dev/null @@ -1,52 +0,0 @@ -// -// LiteCoreOrderingExpressionVisitor.cs -// -// Copyright (c) 2017 Couchbase, Inc All rights reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// -#if CBL_LINQ -using System.Collections.Generic; -using System.Linq; -using System.Linq.Expressions; - -using Remotion.Linq.Clauses; - -namespace Couchbase.Lite.Internal.Linq -{ - internal sealed class LiteCoreOrderingExpressionVisitor : LiteCoreExpressionVisitor - { - #region Public Methods - - public static IList GetJsonExpression(Expression expression, OrderingDirection direction) - { - var visitor = new LiteCoreOrderingExpressionVisitor(); - visitor.Visit(expression); - return visitor.GetJsonExpression(direction); - } - - public IList GetJsonExpression(OrderingDirection direction) - { - var retVal = _query.First() as IList; - if (direction == OrderingDirection.Asc) { - return retVal; - } - - retVal.Insert(0, "DESC"); - return new List { retVal }; - } - - #endregion - } -} -#endif \ No newline at end of file diff --git a/src/Couchbase.Lite.Shared/Linq/LiteCoreQueryExecutor.cs b/src/Couchbase.Lite.Shared/Linq/LiteCoreQueryExecutor.cs deleted file mode 100644 index bddc05d66..000000000 --- a/src/Couchbase.Lite.Shared/Linq/LiteCoreQueryExecutor.cs +++ /dev/null @@ -1,135 +0,0 @@ -// -// LiteCoreQueryExecutor.cs -// -// Copyright (c) 2017 Couchbase, Inc All rights reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// -#if CBL_LINQ -using System.Collections; -using System.Collections.Generic; -using System.Linq; - -using Couchbase.Lite.Internal.Query; -using Couchbase.Lite.Internal.Serialization; -using Couchbase.Lite.Linq; - -using LiteCore; -using LiteCore.Interop; - -using Newtonsoft.Json; - -using Remotion.Linq; - -namespace Couchbase.Lite.Internal.Linq -{ - internal class LiteCoreQueryExecutor : IQueryExecutor - { - #region Variables - - private readonly Database _db; - private unsafe C4Query* _query; - private unsafe C4QueryEnumerator* _queryEnum; - - #endregion - - #region Constructors - - internal LiteCoreQueryExecutor(Database db) - { - _db = db; - } - - #endregion - - #region IQueryExecutor - - public IEnumerable ExecuteCollection(QueryModel queryModel) - { - var visitor = new LiteCoreQueryModelVisitor(); - visitor.VisitQueryModel(queryModel); - var query = visitor.GetJsonQuery(); - CreateQuery(query); - while (MoveNext()) { - yield return GetCurrent(visitor.SelectResult); - } - } - - public T ExecuteScalar(QueryModel queryModel) - { - return ExecuteCollection(queryModel).Single(); - } - - public T ExecuteSingle(QueryModel queryModel, bool returnDefaultWhenEmpty) - { - var sequence = ExecuteCollection(queryModel); - - return returnDefaultWhenEmpty ? sequence.SingleOrDefault() : sequence.Single(); - } - - #endregion - - private unsafe void CreateQuery(string queryStr) - { - _query = (C4Query*)LiteCoreBridge.Check(err => Native.c4query_new(_db.c4db, queryStr, err)); - _queryEnum = (C4QueryEnumerator*) LiteCoreBridge.Check(err => - { - var opts = C4QueryOptions.Default; - return Native.c4query_run(_query, &opts, null, err); - }); - } - - private unsafe bool MoveNext() => Native.c4queryenum_next(_queryEnum, null); - - private unsafe T GetCurrent(ISelectResultContainer resultContainer) - { - if (resultContainer != null) { - resultContainer.Populate(_queryEnum->columns, _db.SharedStrings); - return (T)resultContainer.Results; - } else { - var val = Native.FLArrayIterator_GetValueAt(&_queryEnum->columns, 0); - using (var reader = new JsonFLValueReader(val, _db.SharedStrings)) { - var serializer = JsonSerializer.CreateDefault(); - return serializer.Deserialize(reader); - } - } - } - } - - internal sealed class LiteCoreDebugExecutor : IQueryExecutor - { - #region IQueryExecutor - - public IEnumerable ExecuteCollection(QueryModel queryModel) - { - var visitor = new LiteCoreQueryModelVisitor(); - visitor.VisitQueryModel(queryModel); - return new[] { visitor.GetJsonQuery() } as IEnumerable; - } - - public T ExecuteScalar(QueryModel queryModel) - { - return ExecuteCollection(queryModel).Single(); - } - - public T ExecuteSingle(QueryModel queryModel, bool returnDefaultWhenEmpty) - { - var sequence = ExecuteCollection(queryModel); - - return returnDefaultWhenEmpty ? sequence.SingleOrDefault() : sequence.Single(); - } - - #endregion - } -} -#endif \ No newline at end of file diff --git a/src/Couchbase.Lite.Shared/Linq/LiteCoreQueryModelVisitor.cs b/src/Couchbase.Lite.Shared/Linq/LiteCoreQueryModelVisitor.cs deleted file mode 100644 index 272b34100..000000000 --- a/src/Couchbase.Lite.Shared/Linq/LiteCoreQueryModelVisitor.cs +++ /dev/null @@ -1,174 +0,0 @@ -// -// LiteCoreQueryModelVisitor.cs -// -// Copyright (c) 2017 Couchbase, Inc All rights reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// -// -#if CBL_LINQ -using System; -using System.Collections; -using System.Collections.Generic; -using System.Linq; -using System.Linq.Expressions; - -using Couchbase.Lite.Linq; -using Couchbase.Lite.Util; - -using LiteCore.Interop; -using Newtonsoft.Json; -using Remotion.Linq; -using Remotion.Linq.Clauses; -using Remotion.Linq.Clauses.ResultOperators; - -namespace Couchbase.Lite.Internal.Linq -{ - internal sealed class LiteCoreQueryModelVisitor : QueryModelVisitorBase - { - #region Variables - - private readonly IDictionary _query = new Dictionary(); - - #endregion - - public ISelectResultContainer SelectResult { get; private set; } - - #region Public Methods - - public static string GenerateJsonQuery(QueryModel model) - { - var visitor = new LiteCoreQueryModelVisitor(); - visitor.VisitQueryModel(model); - return visitor.GetJsonQuery(); - } - - #endregion - - #region Internal Methods - - internal unsafe string GetJsonQuery() - { - var json5 = JsonConvert.SerializeObject(_query); - return Native.FLJSON5_ToJSON(json5, null); - } - - #endregion - - #region Overrides - - public override void VisitWhereClause(WhereClause whereClause, QueryModel queryModel, int index) - { - _query["WHERE"] = LiteCoreWhereExpressionVisitor.GetJsonExpression(whereClause.Predicate); - base.VisitWhereClause(whereClause, queryModel, index); - } - - public override void VisitOrdering(Ordering ordering, QueryModel queryModel, OrderByClause orderByClause, int index) - { - var masterList = new List(); - foreach (var o in orderByClause.Orderings) { - masterList.AddRange(LiteCoreOrderingExpressionVisitor.GetJsonExpression(o.Expression, o.OrderingDirection)); - } - - _query["ORDER_BY"] = masterList; - base.VisitOrdering(ordering, queryModel, orderByClause, index); - } - - public override void VisitSelectClause(SelectClause selectClause, QueryModel queryModel) - { - var visitor = new LiteCoreSelectExpressionVisitor(queryModel.BodyClauses.Count > 1); - - visitor.Visit(selectClause.Selector); - _query["WHAT"] = visitor.GetJsonExpression(); - SelectResult = visitor.SelectResult; - - base.VisitSelectClause(selectClause, queryModel); - } - - public override void VisitResultOperator(ResultOperatorBase resultOperator, QueryModel queryModel, int index) - { - switch (resultOperator) { - case DistinctResultOperator ro: - _query["DISTINCT"] = true; - return; - case AverageResultOperator ro: - { - var select = (_query["WHAT"] as List); - select.Insert(0, "AVG()"); - _query["WHAT"] = new[] { select }; - return; - } - case CountResultOperator ro: - { - var select = (_query["WHAT"] as List); - select.Insert(0, "COUNT()"); - _query["WHAT"] = new[] { select }; - return; - } - case MinResultOperator ro: - { - var select = (_query["WHAT"] as List); - select.Insert(0, "MIN()"); - _query["WHAT"] = new[] { select }; - return; - } - case MaxResultOperator ro: - { - var select = (_query["WHAT"] as List); - select.Insert(0, "MAX()"); - _query["WHAT"] = new[] { select }; - return; - } - case SumResultOperator ro: - { - var select = (_query["WHAT"] as List); - select.Insert(0, "SUM()"); - _query["WHAT"] = new[] { select }; - return; - } - case TakeResultOperator ro: - _query["LIMIT"] = ro.GetConstantCount(); - return; - case SkipResultOperator ro: - _query["OFFSET"] = ro.GetConstantCount(); - return; - } - - throw new NotSupportedException($"Result operator {resultOperator.GetType().Name.Replace("ResultOperator","")} not supported"); - } - - public override void VisitJoinClause(JoinClause joinClause, QueryModel queryModel, int index) - { - if (!_query.TryGetValue("FROM", out List> fromList)) { - var mainDb = queryModel.MainFromClause.ItemName; - fromList = new List> { new Dictionary { ["as"] = mainDb } }; - _query["FROM"] = fromList; - } - - var innerPath = LiteCoreSelectExpressionVisitor.GetJsonExpression(joinClause.InnerKeySelector, true)[0]; - var outerPath = LiteCoreSelectExpressionVisitor.GetJsonExpression(joinClause.OuterKeySelector, true)[0]; - - - fromList.Add(new Dictionary - { - ["as"] = joinClause.ItemName, - ["on"] = new[] { "=", outerPath, innerPath } - }); - - base.VisitJoinClause(joinClause, queryModel, index); - } - - #endregion - } -} -#endif \ No newline at end of file diff --git a/src/Couchbase.Lite.Shared/Linq/LiteCoreSelectExpressionVisitor.cs b/src/Couchbase.Lite.Shared/Linq/LiteCoreSelectExpressionVisitor.cs deleted file mode 100644 index 86a1abfbf..000000000 --- a/src/Couchbase.Lite.Shared/Linq/LiteCoreSelectExpressionVisitor.cs +++ /dev/null @@ -1,89 +0,0 @@ -// -// LiteCoreSelectExpressionVisitor.cs -// -// Copyright (c) 2017 Couchbase, Inc All rights reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// -#if CBL_LINQ -using System; -using System.Collections; -using System.Collections.Generic; -using System.Linq.Expressions; - -using Couchbase.Lite.Internal.Linq; - -using JetBrains.Annotations; - -using Remotion.Linq.Clauses.Expressions; - -namespace Couchbase.Lite.Linq -{ - internal sealed class LiteCoreSelectExpressionVisitor : LiteCoreExpressionVisitor - { - - public LiteCoreSelectExpressionVisitor(bool includeDbNames) - { - IncludeDbNames = includeDbNames; - } - - #region Public Methods - - public static IList GetJsonExpression(Expression expression, bool includeDbNames) - { - var visitor = new LiteCoreSelectExpressionVisitor(includeDbNames); - visitor.Visit(expression); - return visitor.GetJsonExpression(); - } - - public IList GetJsonExpression() - { - return _query; - } - - #endregion - - public ISelectResultContainer SelectResult { get; private set; } - - protected override Expression VisitExtension(Expression node) - { - if (node is QuerySourceReferenceExpression exp) { - _query.Add(new[] { "." }); - return node; - } - - return base.VisitExtension(node); - } - - protected override Expression VisitNewArray(NewArrayExpression node) - { - SelectResult = new SelectResultListContainer(node.Type.GetElementType()); - foreach (var expr in node.Expressions) { - Visit(expr); - } - - return node; - } - - protected override Expression VisitNew(NewExpression node) - { - SelectResult = new SelectResultAnonymousContainer(node.Constructor); - foreach (var expr in node.Arguments) { - Visit(expr); - } - - return node; - } - } -} -#endif \ No newline at end of file diff --git a/src/Couchbase.Lite.Shared/Linq/LiteCoreWhereExpressionVisitor.cs b/src/Couchbase.Lite.Shared/Linq/LiteCoreWhereExpressionVisitor.cs deleted file mode 100644 index 003706346..000000000 --- a/src/Couchbase.Lite.Shared/Linq/LiteCoreWhereExpressionVisitor.cs +++ /dev/null @@ -1,60 +0,0 @@ -// -// LiteCoreExpressionTreeVisitor.cs -// -// Copyright (c) 2017 Couchbase, Inc All rights reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// -#if CBL_LINQ -using System; -using System.Collections.Generic; -using System.Diagnostics.CodeAnalysis; -using System.Linq; -using System.Linq.Expressions; -using System.Text; - -using Remotion.Linq.Clauses; -using Remotion.Linq.Clauses.Expressions; -using Remotion.Linq.Clauses.ResultOperators; - -namespace Couchbase.Lite.Internal.Linq -{ - internal sealed class LiteCoreWhereExpressionVisitor : LiteCoreExpressionVisitor - { - - - #region Public Methods - - public static IList GetJsonExpression(Expression expression) - { - var visitor = new LiteCoreWhereExpressionVisitor(); - visitor.Visit(expression); - return visitor.GetJsonExpression(); - } - - public IList GetJsonExpression() - { - if(_query.Count > 1) { - _query.Insert(0, "AND"); - } - - return _query.First() as IList; - } - - #endregion - - - - } -} -#endif \ No newline at end of file diff --git a/src/Couchbase.Lite.Shared/Linq/NotSupportedExpressionVisitor.cs b/src/Couchbase.Lite.Shared/Linq/NotSupportedExpressionVisitor.cs deleted file mode 100644 index bc73f0b46..000000000 --- a/src/Couchbase.Lite.Shared/Linq/NotSupportedExpressionVisitor.cs +++ /dev/null @@ -1,367 +0,0 @@ -// -// NotSupportedExpressionVisitor.cs -// -// Copyright (c) 2016 Couchbase, Inc All rights reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// -#if CBL_LINQ -using System; -using System.Linq.Expressions; - -namespace Couchbase.Lite.Internal.Linq -{ - internal abstract class NotSupportedExpressionVisitor : ExpressionVisitor - { - #region Variables - - protected bool FromSubclass; - - #endregion - - #region Overrides - - protected override Expression VisitBinary(BinaryExpression node) - { - if(FromSubclass) { - FromSubclass = false; - return base.VisitBinary(node); - } - - throw new NotSupportedException(); - } - - protected override Expression VisitBlock(BlockExpression node) - { - if(FromSubclass) { - FromSubclass = false; - return base.VisitBlock(node); - } - - throw new NotSupportedException(); - } - - protected override CatchBlock VisitCatchBlock(CatchBlock node) - { - if(FromSubclass) { - FromSubclass = false; - return base.VisitCatchBlock(node); - } - - throw new NotSupportedException(); - } - - protected override Expression VisitConditional(ConditionalExpression node) - { - if(FromSubclass) { - FromSubclass = false; - return base.VisitConditional(node); - } - - throw new NotSupportedException(); - } - - protected override Expression VisitConstant(ConstantExpression node) - { - if(FromSubclass) { - FromSubclass = false; - return base.VisitConstant(node); - } - - throw new NotSupportedException(); - } - - protected override Expression VisitDebugInfo(DebugInfoExpression node) - { - if(FromSubclass) { - FromSubclass = false; - return base.VisitDebugInfo(node); - } - - throw new NotSupportedException(); - } - - protected override Expression VisitDefault(DefaultExpression node) - { - if(FromSubclass) { - FromSubclass = false; - return base.VisitDefault(node); - } - - throw new NotSupportedException(); - } - - protected override ElementInit VisitElementInit(ElementInit node) - { - if(FromSubclass) { - FromSubclass = false; - return base.VisitElementInit(node); - } - - throw new NotSupportedException(); - } - - protected override Expression VisitExtension(Expression node) - { - if(FromSubclass) { - FromSubclass = false; - return base.VisitExtension(node); - } - - throw new NotSupportedException(); - } - - protected override Expression VisitGoto(GotoExpression node) - { - if(FromSubclass) { - FromSubclass = false; - return base.VisitGoto(node); - } - - throw new NotSupportedException(); - } - - protected override Expression VisitIndex(IndexExpression node) - { - if(FromSubclass) { - FromSubclass = false; - return base.VisitIndex(node); - } - - throw new NotSupportedException(); - } - - protected override Expression VisitInvocation(InvocationExpression node) - { - if(FromSubclass) { - FromSubclass = false; - return base.VisitInvocation(node); - } - - throw new NotSupportedException(); - } - - protected override Expression VisitLabel(LabelExpression node) - { - if(FromSubclass) { - FromSubclass = false; - return base.VisitLabel(node); - } - - throw new NotSupportedException(); - } - - protected override LabelTarget VisitLabelTarget(LabelTarget node) - { - if(FromSubclass) { - FromSubclass = false; - return base.VisitLabelTarget(node); - } - - throw new NotSupportedException(); - } - - protected override Expression VisitLambda(Expression node) - { - if(FromSubclass) { - FromSubclass = false; - return base.VisitLambda(node); - } - - throw new NotSupportedException(); - } - - protected override Expression VisitListInit(ListInitExpression node) - { - if(FromSubclass) { - FromSubclass = false; - return base.VisitListInit(node); - } - - throw new NotSupportedException(); - } - - protected override Expression VisitLoop(LoopExpression node) - { - if(FromSubclass) { - FromSubclass = false; - return base.VisitLoop(node); - } - - throw new NotSupportedException(); - } - - protected override Expression VisitMember(MemberExpression node) - { - if(FromSubclass) { - FromSubclass = false; - return base.VisitMember(node); - } - - throw new NotSupportedException(); - } - - protected override MemberAssignment VisitMemberAssignment(MemberAssignment node) - { - if(FromSubclass) { - FromSubclass = false; - return base.VisitMemberAssignment(node); - } - - throw new NotSupportedException(); - } - - protected override MemberBinding VisitMemberBinding(MemberBinding node) - { - if(FromSubclass) { - FromSubclass = false; - return base.VisitMemberBinding(node); - } - - throw new NotSupportedException(); - } - - protected override Expression VisitMemberInit(MemberInitExpression node) - { - if(FromSubclass) { - FromSubclass = false; - return base.VisitMemberInit(node); - } - - throw new NotSupportedException(); - } - - protected override MemberListBinding VisitMemberListBinding(MemberListBinding node) - { - if(FromSubclass) { - FromSubclass = false; - return base.VisitMemberListBinding(node); - } - - throw new NotSupportedException(); - } - - protected override MemberMemberBinding VisitMemberMemberBinding(MemberMemberBinding node) - { - if(FromSubclass) { - FromSubclass = false; - return base.VisitMemberMemberBinding(node); - } - - throw new NotSupportedException(); - } - - protected override Expression VisitMethodCall(MethodCallExpression node) - { - if(FromSubclass) { - FromSubclass = false; - return base.VisitMethodCall(node); - } - - throw new NotSupportedException($"Method {node.Method.Name} not supported"); - } - - protected override Expression VisitNew(NewExpression node) - { - if(FromSubclass) { - FromSubclass = false; - return base.VisitNew(node); - } - - throw new NotSupportedException(); - } - - protected override Expression VisitNewArray(NewArrayExpression node) - { - if(FromSubclass) { - FromSubclass = false; - return base.VisitNewArray(node); - } - - throw new NotSupportedException(); - } - - protected override Expression VisitParameter(ParameterExpression node) - { - if(FromSubclass) { - FromSubclass = false; - return base.VisitParameter(node); - } - - throw new NotSupportedException(); - } - - protected override Expression VisitRuntimeVariables(RuntimeVariablesExpression node) - { - if(FromSubclass) { - FromSubclass = false; - return base.VisitRuntimeVariables(node); - } - - throw new NotSupportedException(); - } - - protected override Expression VisitSwitch(SwitchExpression node) - { - if(FromSubclass) { - FromSubclass = false; - return base.VisitSwitch(node); - } - - throw new NotSupportedException(); - } - - protected override SwitchCase VisitSwitchCase(SwitchCase node) - { - if(FromSubclass) { - FromSubclass = false; - return base.VisitSwitchCase(node); - } - - throw new NotSupportedException(); - } - - protected override Expression VisitTry(TryExpression node) - { - if(FromSubclass) { - FromSubclass = false; - return base.VisitTry(node); - } - - throw new NotSupportedException(); - } - - protected override Expression VisitTypeBinary(TypeBinaryExpression node) - { - if(FromSubclass) { - FromSubclass = false; - return base.VisitTypeBinary(node); - } - - throw new NotSupportedException(); - } - - protected override Expression VisitUnary(UnaryExpression node) - { - if(FromSubclass) { - FromSubclass = false; - return base.VisitUnary(node); - } - - throw new NotSupportedException(); - } - - #endregion - } -} -#endif \ No newline at end of file diff --git a/src/Couchbase.Lite.Shared/Log/LogJsonString.cs b/src/Couchbase.Lite.Shared/Log/LogJsonString.cs index 8ba8c17a0..31e72767d 100644 --- a/src/Couchbase.Lite.Shared/Log/LogJsonString.cs +++ b/src/Couchbase.Lite.Shared/Log/LogJsonString.cs @@ -20,8 +20,7 @@ using System.Diagnostics.CodeAnalysis; using System.Linq; using System.Text; - -using Newtonsoft.Json; +using System.Text.Json; namespace Couchbase.Lite.Logging; @@ -48,5 +47,5 @@ internal sealed class LogJsonString(object obj) { private string? _serialized; - public override string ToString() => _serialized ??= JsonConvert.SerializeObject(obj); + public override string ToString() => _serialized ??= JsonSerializer.Serialize(obj); } \ No newline at end of file diff --git a/src/Couchbase.Lite.Shared/Log/SecureLogString.cs b/src/Couchbase.Lite.Shared/Log/SecureLogString.cs index 6b4a1a6be..c16c80594 100644 --- a/src/Couchbase.Lite.Shared/Log/SecureLogString.cs +++ b/src/Couchbase.Lite.Shared/Log/SecureLogString.cs @@ -15,9 +15,10 @@ // See the License for the specific language governing permissions and // limitations under the License. // -using Newtonsoft.Json; + using System.Linq; using System.Text; +using System.Text.Json; namespace Couchbase.Lite.Logging; @@ -93,7 +94,7 @@ private string String return _str; } - var str = JsonConvert.SerializeObject(_object); + var str = JsonSerializer.Serialize(_object); _str = str.Length > 100 ? $"{new string(str.Take(100).ToArray())}..." : str; return _str; diff --git a/src/Couchbase.Lite.Shared/Query/DatabaseQueryable.cs b/src/Couchbase.Lite.Shared/Query/DatabaseQueryable.cs deleted file mode 100644 index 870068c79..000000000 --- a/src/Couchbase.Lite.Shared/Query/DatabaseQueryable.cs +++ /dev/null @@ -1,72 +0,0 @@ -// -// DatabaseQueryable.cs -// -// Copyright (c) 2017 Couchbase, Inc All rights reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// - -#if CBL_LINQ -using System.Linq; -using System.Linq.Expressions; -using Couchbase.Lite.Internal.Linq; -using Couchbase.Lite.Linq; - -using Remotion.Linq; -using Remotion.Linq.Parsing.Structure; - -namespace Couchbase.Lite.Internal.Query -{ - internal sealed class DatabaseQueryable : QueryableBase - { - #region Constructors - - public DatabaseQueryable(Database db) - : base(QueryParser.CreateDefault(), new LiteCoreQueryExecutor(db)) - { - - } - - public DatabaseQueryable(IQueryProvider provider, Expression expression) - : base(provider, expression) - { - } - - #endregion - } - - internal sealed class DatabaseDebugQueryable : QueryableBase - { - #region Constructors - - public DatabaseDebugQueryable() - : this(QueryParser.CreateDefault(), new LiteCoreDebugExecutor()) - { - - } - - public DatabaseDebugQueryable(IQueryParser queryParser, IQueryExecutor executor) - : base(new DefaultQueryProvider(typeof(DatabaseQueryable<>), queryParser, executor)) - { - - } - - public DatabaseDebugQueryable(IQueryProvider provider, Expression expression) - : base(provider, expression) - { - } - - #endregion - } -} -#endif \ No newline at end of file diff --git a/src/Couchbase.Lite.Shared/Query/QueryExpression.cs b/src/Couchbase.Lite.Shared/Query/QueryExpression.cs index e4c643b69..48eb25b74 100644 --- a/src/Couchbase.Lite.Shared/Query/QueryExpression.cs +++ b/src/Couchbase.Lite.Shared/Query/QueryExpression.cs @@ -19,13 +19,12 @@ using System; using System.Collections; using System.Collections.Generic; +using System.Text.Json; using Couchbase.Lite.Internal.Logging; using Couchbase.Lite.Query; using Couchbase.Lite.Util; -using Newtonsoft.Json; - namespace Couchbase.Lite.Internal.Query; internal abstract class QueryExpression : IExpression @@ -108,7 +107,7 @@ private QueryExpression GetOperator(BinaryOpType type, IExpression expression) public override string ToString() { - return $"[{GetType().Name}] => {JsonConvert.SerializeObject(ConvertToJSON())}"; + return $"[{GetType().Name}] => {JsonSerializer.Serialize(ConvertToJSON())}"; } public IExpression Add(IExpression expression) => diff --git a/src/Couchbase.Lite.Shared/Query/XQuery.cs b/src/Couchbase.Lite.Shared/Query/XQuery.cs index 4d932aa02..37453a517 100644 --- a/src/Couchbase.Lite.Shared/Query/XQuery.cs +++ b/src/Couchbase.Lite.Shared/Query/XQuery.cs @@ -20,13 +20,12 @@ using System.Collections.Generic; using System.Diagnostics; using System.Runtime.CompilerServices; - +using System.Text.Json; using Couchbase.Lite.Internal.Logging; using Couchbase.Lite.Query; using Couchbase.Lite.Util; using LiteCore; using LiteCore.Interop; -using Newtonsoft.Json; namespace Couchbase.Lite.Internal.Query; @@ -224,7 +223,7 @@ private string EncodeAsJSON() parameters["HAVING"] = HavingImpl.ToJSON(); } - return JsonConvert.SerializeObject(parameters); + return JsonSerializer.Serialize(parameters); } private void Check() diff --git a/src/Couchbase.Lite.Shared/Serialization/JsonFLValueReader.cs b/src/Couchbase.Lite.Shared/Serialization/JsonFLValueReader.cs deleted file mode 100644 index 875b6e15b..000000000 --- a/src/Couchbase.Lite.Shared/Serialization/JsonFLValueReader.cs +++ /dev/null @@ -1,257 +0,0 @@ -// -// JsonFLValueReader.cs -// -// Copyright (c) 2015 Couchbase, Inc All rights reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// -#if CBL_LINQ -using System; -using Newtonsoft.Json; -using System.Collections.Generic; - -using Couchbase.Lite.Logging; -using LiteCore; -using LiteCore.Interop; -using System.Globalization; - -namespace Couchbase.Lite.Internal.Serialization -{ - internal sealed unsafe class JsonFLValueReader : JsonReader - { - #region Constants - - private const string Tag = nameof(JsonFLValueReader); - - #endregion - - #region Variables - - private readonly Stack _sequenceStack = new Stack(); - private readonly SharedStringCache _stringCache; - private FLValue* _currentValue; - private bool _inValue; - - #endregion - - #region Constructors - - public JsonFLValueReader(FLValue *root, SharedStringCache stringCache) - { - _currentValue = root; - _stringCache = stringCache; - } - - #endregion - - #region Private Methods - - private void BeginArray(FLArray* a) - { - FLArrayIterator i; - Native.FLArrayIterator_Begin(a, &i); - _sequenceStack.Push(i); - } - - private void BeginObject(FLDict* d) - { - FLDictIterator i; - Native.FLDictIterator_Begin(d, &i); - _sequenceStack.Push(i); - } - - private string GetKey() - { - string key; - if(Native.FLValue_GetType(_currentValue) == FLValueType.Number) { - key = _stringCache.GetKey((int)Native.FLValue_AsInt(_currentValue)); - if(key == null) { - WriteLog.To.Database.W(Tag, "Corrupt key found during deserialization, skipping..."); - } - } else { - key = Native.FLValue_AsString(_currentValue); - } - - return key; - } - - private NextActionCode NextAction() - { - if(_sequenceStack.Count == 0) { - return NextActionCode.ReadValue; - } - - var i = _sequenceStack.Pop(); - if(i is FLDictIterator) { - var iter = (FLDictIterator)i; - if(_inValue) { - _inValue = false; - _currentValue = Native.FLDictIterator_GetValue(&iter); - if(_currentValue == null) { - return NextActionCode.EndObject; - } - - Native.FLDictIterator_Next(&iter); - _sequenceStack.Push(iter); - return NextActionCode.ReadValue; - } - - _currentValue = Native.FLDictIterator_GetKey(&iter); - if(_currentValue == null) { - return NextActionCode.EndObject; - } - - _inValue = true; - _sequenceStack.Push(i); - return NextActionCode.ReadObjectKey; - } else { - var iter = (FLArrayIterator)i; - _currentValue = Native.FLArrayIterator_GetValue(&iter); - if(_currentValue == null) { - return NextActionCode.EndArray; - } - - Native.FLArrayIterator_Next(&iter); - _sequenceStack.Push(iter); - return NextActionCode.ReadValue; - } - } - - #endregion - - #region Overrides - - public override bool Read() - { - if(_sequenceStack.Count == 0 && _currentValue == null) { - return false; - } - - switch(NextAction()) { - case NextActionCode.EndArray: - SetToken(JsonToken.EndArray); - break; - case NextActionCode.EndObject: - SetToken(JsonToken.EndObject); - break; - case NextActionCode.ReadObjectKey: - var key = GetKey(); - if(key == null) { - return false; - } - - SetToken(JsonToken.PropertyName, key); - break; - case NextActionCode.ReadValue: - switch(Native.FLValue_GetType(_currentValue)) { - case FLValueType.Array: - BeginArray(Native.FLValue_AsArray(_currentValue)); - SetToken(JsonToken.StartArray); - break; - case FLValueType.Boolean: - SetToken(JsonToken.Boolean, Native.FLValue_AsBool(_currentValue)); - break; - case FLValueType.Dict: - BeginObject(Native.FLValue_AsDict(_currentValue)); - SetToken(JsonToken.StartObject); - break; - case FLValueType.Null: - SetToken(JsonToken.Null, null); - break; - case FLValueType.Number: - if(Native.FLValue_IsInteger(_currentValue)) { - if(Native.FLValue_IsUnsigned(_currentValue)) { - SetToken(JsonToken.Integer, Native.FLValue_AsUnsigned(_currentValue)); - } - - SetToken(JsonToken.Integer, Native.FLValue_AsInt(_currentValue)); - } else if(Native.FLValue_IsDouble(_currentValue)) { - SetToken(JsonToken.Float, Native.FLValue_AsDouble(_currentValue)); - } else { - SetToken(JsonToken.Float, Native.FLValue_AsFloat(_currentValue)); - } - - break; - case FLValueType.String: - SetToken(JsonToken.String, Native.FLValue_AsString(_currentValue)); - break; - default: - return false; - } - break; - default: - return false; - - } - - return true; - } - - public override DateTime? ReadAsDateTime() - { - var action = NextAction(); - if(action != NextActionCode.ReadValue) { - throw new JsonReaderException($"Invalid state for reading date time offset ({action})"); - } - - if(Native.FLValue_GetType(_currentValue) != FLValueType.String) { - return null; - } - - - DateTime retVal; - if(DateTime.TryParseExact(Native.FLValue_AsString(_currentValue), "o", null, DateTimeStyles.RoundtripKind, out retVal)) { - SetToken(JsonToken.Date, retVal); - return retVal; - } - - throw new LiteCoreException(new C4Error(FLError.EncodeError)); - } - - public override DateTimeOffset? ReadAsDateTimeOffset() - { - var action = NextAction(); - if(action != NextActionCode.ReadValue) { - throw new JsonReaderException($"Invalid state for reading date time offset ({action})"); - } - - if(Native.FLValue_GetType(_currentValue) != FLValueType.String) { - return null; - } - - - DateTimeOffset retVal; - if(DateTimeOffset.TryParseExact(Native.FLValue_AsString(_currentValue), "o", null, DateTimeStyles.RoundtripKind, out retVal)) { - SetToken(JsonToken.Date, retVal); - return retVal; - } - - throw new LiteCoreException(new C4Error(FLError.EncodeError)); - } - - #endregion - - #region Nested - - private enum NextActionCode - { - ReadValue, - ReadObjectKey, - EndObject, - EndArray - } - - #endregion - } -} -#endif \ No newline at end of file diff --git a/src/Couchbase.Lite.Shared/Serialization/JsonFLValueWriter.cs b/src/Couchbase.Lite.Shared/Serialization/JsonFLValueWriter.cs deleted file mode 100644 index a4319de9f..000000000 --- a/src/Couchbase.Lite.Shared/Serialization/JsonFLValueWriter.cs +++ /dev/null @@ -1,213 +0,0 @@ -// -// JsonFLValueWriter.cs -// -// Copyright (c) 2015 Couchbase, Inc All rights reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// -#if CBL_LINQ -using System; - -using LiteCore; -using LiteCore.Interop; -using Newtonsoft.Json; - -namespace Couchbase.Lite.Internal.Serialization -{ - internal unsafe class JsonFLValueWriter : JsonWriter - { - #region Variables - - private FLEncoder* _encoder; - - #endregion - - #region Properties - - public FLSliceResult Result { get; private set; } - - #endregion - - #region Constructors - - public JsonFLValueWriter(C4Database* db) - { - _encoder = Native.c4db_createFleeceEncoder(db); - } - - #endregion - - #region Overrides - - protected override void Dispose(bool disposing) - { - base.Dispose(disposing); - Native.FLEncoder_Free(_encoder); - _encoder = null; - } - - public override void Flush() - { - FLError err; - Result = NativeRaw.FLEncoder_Finish(_encoder, &err); - if(Result.buf == null) { - throw new LiteCoreException(new C4Error(err)); - } - } - - public override void WriteEndArray() - { - base.WriteEndArray(); - Native.FLEncoder_EndArray(_encoder); - } - - public override void WriteEndObject() - { - base.WriteEndObject(); - Native.FLEncoder_EndDict(_encoder); - } - - public override void WriteNull() - { - base.WriteNull(); - Native.FLEncoder_WriteNull(_encoder); - } - - public override void WritePropertyName(string name) - { - base.WritePropertyName(name); - Native.FLEncoder_WriteKey(_encoder, name); - } - - public override void WriteStartArray() - { - base.WriteStartArray(); - Native.FLEncoder_BeginArray(_encoder, 0); - } - - public override void WriteStartObject() - { - base.WriteStartObject(); - Native.FLEncoder_BeginDict(_encoder, 0); - } - - public override void WriteValue(bool value) - { - base.WriteValue(value); - Native.FLEncoder_WriteBool(_encoder, value); - } - - public override void WriteValue(byte value) - { - base.WriteValue(value); - Native.FLEncoder_WriteUInt(_encoder, value); - } - - public override void WriteValue(double value) - { - base.WriteValue(value); - Native.FLEncoder_WriteDouble(_encoder, value); - } - - public override void WriteValue(decimal value) - { - base.WriteValue(value); - Native.FLEncoder_WriteDouble(_encoder, (double)value); - } - - public override void WriteValue(float value) - { - base.WriteValue(value); - Native.FLEncoder_WriteFloat(_encoder, value); - } - - public override void WriteValue(int value) - { - base.WriteValue(value); - Native.FLEncoder_WriteInt(_encoder, value); - } - - public override void WriteValue(long value) - { - base.WriteValue(value); - Native.FLEncoder_WriteInt(_encoder, value); - } - - public override void WriteValue(sbyte value) - { - base.WriteValue(value); - Native.FLEncoder_WriteInt(_encoder, value); - } - - public override void WriteValue(short value) - { - base.WriteValue(value); - Native.FLEncoder_WriteInt(_encoder, value); - } - - public override void WriteValue(uint value) - { - base.WriteValue(value); - Native.FLEncoder_WriteUInt(_encoder, value); - } - - public override void WriteValue(ulong value) - { - base.WriteValue(value); - Native.FLEncoder_WriteUInt(_encoder, value); - } - - public override void WriteValue(ushort value) - { - base.WriteValue(value); - Native.FLEncoder_WriteUInt(_encoder, value); - } - - public override void WriteValue(string value) - { - base.WriteValue(value); - Native.FLEncoder_WriteString(_encoder, value); - } - - public override void WriteValue(DateTime value) - { - WriteValue(value.ToString("o")); - } - - public override void WriteValue(DateTime? value) - { - if(!value.HasValue) { - WriteNull(); - } else { - WriteValue(value.Value); - } - } - - public override void WriteValue(DateTimeOffset value) - { - WriteValue(value.ToString("o")); - } - - public override void WriteValue(DateTimeOffset? value) - { - if (!value.HasValue) { - WriteNull(); - } else { - WriteValue(value.Value); - } - } - - #endregion - } -} -#endif \ No newline at end of file diff --git a/src/Couchbase.Lite.Shared/Sync/HTTPLogic.cs b/src/Couchbase.Lite.Shared/Sync/HTTPLogic.cs index 03c34b5b9..c47f0f3c9 100644 --- a/src/Couchbase.Lite.Shared/Sync/HTTPLogic.cs +++ b/src/Couchbase.Lite.Shared/Sync/HTTPLogic.cs @@ -28,6 +28,7 @@ using Couchbase.Lite.Internal.Logging; using LiteCore.Interop; +using Microsoft.Extensions.DependencyInjection; #if !NET8_0_OR_GREATER using Couchbase.Lite.Util; @@ -185,7 +186,7 @@ private static string GetUserAgent() version = match.Groups[1].Value; } - var runtimePlatform = Service.GetInstance(); + var runtimePlatform = Service.Provider.GetService(); var osDescription = runtimePlatform?.OSDescription ?? RuntimeInformation.OSDescription; var hardware = runtimePlatform?.HardwareName != null ? $"; {runtimePlatform.HardwareName}" : ""; return $"CouchbaseLite/{version} (.NET; {osDescription}{hardware}) Build/{build} LiteCore/{Native.c4_getVersion()} Commit/{commit}"; diff --git a/src/Couchbase.Lite.Shared/Sync/WebSocketWrapper.cs b/src/Couchbase.Lite.Shared/Sync/WebSocketWrapper.cs index 65447b6f4..614489be0 100644 --- a/src/Couchbase.Lite.Shared/Sync/WebSocketWrapper.cs +++ b/src/Couchbase.Lite.Shared/Sync/WebSocketWrapper.cs @@ -37,6 +37,7 @@ using Couchbase.Lite.Util; using Dispatch; using LiteCore.Interop; +using Microsoft.Extensions.DependencyInjection; #if NET9_0_OR_GREATER using LockType = System.Threading.Lock; @@ -128,7 +129,7 @@ internal sealed class WebSocketWrapper private BlockingCollection? _writeQueue; private readonly LockType _writeQueueLock = new(); // Used to avoid disposal race - private readonly IReachability _reachability = Service.GetInstance() ?? new Reachability(); + private readonly IReachability _reachability = Service.Provider.GetService() ?? new Reachability(); public Stream? NetworkStream { get; private set; } @@ -227,7 +228,7 @@ public unsafe void Start() // STEP 2.5: The IProxy interface will detect a system-wide proxy that is set // And if it is, it will return an IWebProxy object to use // Sending "CONNECT" request if IWebProxy object is not null - var proxy = Service.GetInstance(); + var proxy = Service.Provider.GetService(); if (_client is { Connected: false } && proxy != null) { ConnectProxyAsync(proxy); } diff --git a/src/Couchbase.Lite.Tests.NetCore/Couchbase.Lite.Tests.NetCore.csproj b/src/Couchbase.Lite.Tests.NetCore/Couchbase.Lite.Tests.NetCore.csproj index 5e336d07b..8db24fa34 100644 --- a/src/Couchbase.Lite.Tests.NetCore/Couchbase.Lite.Tests.NetCore.csproj +++ b/src/Couchbase.Lite.Tests.NetCore/Couchbase.Lite.Tests.NetCore.csproj @@ -28,11 +28,8 @@ - - - all diff --git a/src/Couchbase.Lite.Tests.Shared/ArrayTest.cs b/src/Couchbase.Lite.Tests.Shared/ArrayTest.cs index ee7f42e1e..c3a008453 100644 --- a/src/Couchbase.Lite.Tests.Shared/ArrayTest.cs +++ b/src/Couchbase.Lite.Tests.Shared/ArrayTest.cs @@ -22,11 +22,10 @@ using System.Diagnostics.CodeAnalysis; using System.Linq; using System.Text; +using System.Text.Json; using Couchbase.Lite; using Couchbase.Lite.Internal.Doc; using Shouldly; -using Newtonsoft.Json; -using Newtonsoft.Json.Linq; using Xunit; using Xunit.Abstractions; @@ -1340,12 +1339,13 @@ public void TestTypesInArrayToJSON() jList.Count.ShouldBe(17, "because 17 entries were added"); for (var i = 0; i < count; i++) { if (array[i] != null && array[i] is Blob) { - var b1JsonD = ((JObject)jList[i]).ToObject>()!; + var b1JsonD = jList[i] as Dictionary; + b1JsonD.ShouldNotBeNull(); var b2JsonD = ((Blob?)array[i])!.JsonRepresentation; var blob = new Blob(Db, b1JsonD); blob.ShouldBe((Blob?) array[i]); - + foreach (var kv in b1JsonD) { var hasValue = b2JsonD.TryGetValue(kv.Key, out var gotValue); hasValue.ShouldBeTrue($"because otherwise b2JsonD is missing key '{kv.Key}'"); @@ -1364,7 +1364,7 @@ public void TestTypesInArrayToJSON() public void TestMutableArrayWithJsonString() { var array = PopulateArrayData(); - var arrayJson = JsonConvert.SerializeObject(array, _jsonSerializerSettings); + var arrayJson = JsonSerializer.Serialize(array); var ma = new MutableArrayObject(arrayJson); var cnt = ma.Count(); for (int index=0; index < cnt; index++) { @@ -1424,7 +1424,7 @@ public void TestMutableArraySetJsonWithInvalidParam() //with dict json string var dict = new Dictionary { { "apple", 5 }, { "banana", 2 }, { "orange", 10 } }; - var jDict = JsonConvert.SerializeObject(dict); + var jDict = JsonSerializer.Serialize(dict); ex = Should.Throw(() => ma.SetJSON(jDict)); ex.Message.ShouldBe(CouchbaseLiteErrorMessage.InvalidJSON); } @@ -1445,7 +1445,7 @@ public void TestCreateMutableArrayWithInvalidStr() //with dict json string var dict = new Dictionary { { "apple", 5}, { "banana", 2 }, { "orange", 10 } }; - var jDict = JsonConvert.SerializeObject(dict); + var jDict = JsonSerializer.Serialize(dict); ex = Should.Throw(() => new MutableArrayObject(jDict)); ex.Message.ShouldBe(CouchbaseLiteErrorMessage.InvalidJSON); } diff --git a/src/Couchbase.Lite.Tests.Shared/BlobTest.cs b/src/Couchbase.Lite.Tests.Shared/BlobTest.cs index e8404f531..0c66da35b 100644 --- a/src/Couchbase.Lite.Tests.Shared/BlobTest.cs +++ b/src/Couchbase.Lite.Tests.Shared/BlobTest.cs @@ -21,14 +21,13 @@ using System.IO; using System.Reflection; using System.Text; +using System.Text.Json; using Couchbase.Lite; using Couchbase.Lite.Internal.Doc; using Shouldly; using LiteCore; using LiteCore.Interop; -using Newtonsoft.Json; -using Newtonsoft.Json.Linq; using Xunit; using Xunit.Abstractions; @@ -142,7 +141,7 @@ public void TestBlobToJSON() var json = blob.ToJSON(); - var blobFromJStr = JsonConvert.DeserializeObject>(json); + var blobFromJStr = DataOps.ParseTo>(json); blobFromJStr.ShouldNotBeNull("because otherwise the blob JSON was invalid"); blob.JSONEquals(blobFromJStr!).ShouldBeTrue(); } @@ -188,7 +187,7 @@ public void TestDocumentBlobToJSON() var b = d.GetBlob("blob"); b.ShouldNotBeNull("because it was saved into the document"); var json = b.ToJSON(); - var blobFromJson = JsonConvert.DeserializeObject>(json); + var blobFromJson = DataOps.ParseTo>(json); blobFromJson.ShouldNotBeNull("because otherwise the blob JSON was invalid"); blobFromJson.ShouldContainKeys(Blob.ContentTypeKey, Blob.DigestKey, Blob.LengthKey, Constants.ObjectTypeProperty); } @@ -220,8 +219,7 @@ public void TestDbBlobToJson() var blobJson = blob.ToJSON(); - var o = JObject.Parse(blobJson); - var mDictFromJObj = o.ToObject>(); + var mDictFromJObj = DataOps.ParseTo>(blobJson); using (var cbDoc = new MutableDocument("doc1")) { cbDoc.SetValue("dict", mDictFromJObj); @@ -232,9 +230,9 @@ public void TestDbBlobToJson() gotBlob.ShouldNotBeNull(); var newJson = gotBlob.ToJSON(); - var blobJsonD = JsonConvert.DeserializeObject>(blobJson); + var blobJsonD = DataOps.ParseTo>(blobJson); blobJsonD.ShouldNotBeNull("because otherwise the pre-save blob JSON was invalid"); - var newJsonD = JsonConvert.DeserializeObject>(newJson); + var newJsonD = DataOps.ParseTo>(newJson); newJsonD.ShouldNotBeNull("because otherwise the post-save blob JSON was invalid"); foreach (var kv in blobJsonD) { @@ -256,7 +254,7 @@ public void TestAccessContentFromBlobCreatedFromJson() { Constants.ObjectTypeProperty, "blob" } }}; - var listContainsBlobJson = JsonConvert.SerializeObject(blobDict); + var listContainsBlobJson = JsonSerializer.Serialize(blobDict); using var md = new MutableDocument("doc1"); var ma = new MutableArrayObject(listContainsBlobJson); var blobInMa = (MutableDictionaryObject?)ma.GetValue(0); @@ -286,7 +284,7 @@ public void TestBlobJsonStringInLayersOfMutableDict() { "blobUnderArr", new List() { b1, b2, b3 } } }; - var dicJson = JsonConvert.SerializeObject(keyValueDictionary); + var dicJson = JsonSerializer.Serialize(keyValueDictionary); var md = new MutableDictionaryObject(dicJson); using (var mdoc = new MutableDocument("doc1")) { mdoc.SetDictionary("dict", md); diff --git a/src/Couchbase.Lite.Tests.Shared/CSharpTest.cs b/src/Couchbase.Lite.Tests.Shared/CSharpTest.cs index cc935d6b6..11bc53fcb 100644 --- a/src/Couchbase.Lite.Tests.Shared/CSharpTest.cs +++ b/src/Couchbase.Lite.Tests.Shared/CSharpTest.cs @@ -37,8 +37,6 @@ using Shouldly; using LiteCore.Interop; -using Newtonsoft.Json; -using Newtonsoft.Json.Linq; using Extensions = Couchbase.Lite.Util.Extensions; using Couchbase.Lite.Internal.Logging; @@ -47,7 +45,7 @@ using System.IO; using System.Linq; using System.Reflection; -using System.Security.Cryptography.X509Certificates; +using System.Text.Json; using Xunit; using Xunit.Abstractions; @@ -63,6 +61,10 @@ using Couchbase.Lite.DI; #endif +#if NET9_0_OR_GREATER +using System.Security.Cryptography.X509Certificates; +#endif + namespace Test { // NOTE: This tests classes specific to CSharp binding to LiteCore, it does not @@ -284,15 +286,10 @@ public void TestHttpMessageParserWithString() public unsafe void TestSerializationRoundTrip() { var masterList = new List>(); - var settings = new JsonSerializerSettings { - DateParseHandling = DateParseHandling.None - }; - var s = JsonSerializer.CreateDefault(settings); ReadFileByLines("C/tests/data/iTunesMusicLibrary.json", line => { - using var reader = new JsonTextReader(new StringReader(line)); - var gotten = s.Deserialize>(reader); + var gotten = DataOps.ParseTo>(line); gotten.ShouldNotBeNull("because otherwise the JSON on disk was corrupt"); masterList.Add(gotten!); @@ -532,29 +529,6 @@ public async Task TestSerialQueue() } #endif - [Fact] - public void TestAutoConvertJson() - { - var jVal = new JValue("test"); - var jArray = new JArray(jVal); - var jObj = new JObject { ["test"] = jVal }; - - DataOps.ToCouchbaseObject(jVal).ShouldBe("test"); - (DataOps.ToCouchbaseObject(jArray) as MutableArrayObject)?[0].String.ShouldBe("test"); - (DataOps.ToCouchbaseObject(jObj) as MutableDictionaryObject)?["test"].String.ShouldBe("test"); - - const string JSONString = """{"level1":{"foo":"bar"},"level2":{"list":[1, 3.14, "s"]}, "$type":"JSON .NET Object"}"""; - var json = JsonConvert.DeserializeObject>(JSONString); - var converted = DataOps.ToCouchbaseObject(json) as MutableDictionaryObject; - converted.ShouldNotBeNull(); - - converted["level1"]["foo"].String.ShouldBe("bar"); - converted["level2"]["list"][0].Int.ShouldBe(1); - converted["level2"]["list"][1].Double.ShouldBe(3.14); - converted["level2"]["list"][2].String.ShouldBe("s"); - converted["$type"].String.ShouldBe("JSON .NET Object"); - } - [Fact] public void TestCreateExceptions() { @@ -603,7 +577,7 @@ public void TestDictionaryWithULong() [Fact] public async Task TestMainThreadScheduler() { - var scheduler = Service.GetInstance(); + var scheduler = Service.Provider.GetService(); if(scheduler == null) { return; } diff --git a/src/Couchbase.Lite.Tests.Shared/DictionaryTest.cs b/src/Couchbase.Lite.Tests.Shared/DictionaryTest.cs index 187eb4803..e88fb7f95 100644 --- a/src/Couchbase.Lite.Tests.Shared/DictionaryTest.cs +++ b/src/Couchbase.Lite.Tests.Shared/DictionaryTest.cs @@ -19,11 +19,11 @@ using System; using System.Collections.Generic; using System.Text; +using System.Text.Json; using Couchbase.Lite; using Couchbase.Lite.Internal.Doc; using Shouldly; -using Newtonsoft.Json; using Xunit; using Xunit.Abstractions; @@ -536,7 +536,7 @@ public void TestTypesInDictionaryToJSON() public void TestMutableDictWithJsonString() { var dic = PopulateDictData(); - var dicJson = JsonConvert.SerializeObject(dic, _jsonSerializerSettings); + var dicJson = JsonSerializer.Serialize(dic); var md = new MutableDictionaryObject(dicJson); ValidateValuesInMutableDictFromJson(dic, md); @@ -559,7 +559,7 @@ public void TestMutableDictSetJsonWithInvalidParam() //with array json string string[] arr = ["apple", "banana", "orange"]; - var jArr = JsonConvert.SerializeObject(arr); + var jArr = JsonSerializer.Serialize(arr); ex = Should.Throw(() => md.SetJSON(jArr)); ex.Message.ShouldBe(CouchbaseLiteErrorMessage.InvalidJSON); } @@ -573,7 +573,7 @@ public void TestCreateMutableDictWithInvalidStr() //with array json string string[] arr = ["apple", "banana", "orange"]; - var jArr = JsonConvert.SerializeObject(arr); + var jArr = JsonSerializer.Serialize(arr); ex = Should.Throw(() => new MutableDictionaryObject(jArr)); ex.Message.ShouldBe(CouchbaseLiteErrorMessage.InvalidJSON); } diff --git a/src/Couchbase.Lite.Tests.Shared/DocumentTest.cs b/src/Couchbase.Lite.Tests.Shared/DocumentTest.cs index ca1b7c038..8045bdb16 100644 --- a/src/Couchbase.Lite.Tests.Shared/DocumentTest.cs +++ b/src/Couchbase.Lite.Tests.Shared/DocumentTest.cs @@ -21,10 +21,10 @@ using System.IO; using System.Linq; using System.Text; +using System.Text.Json; using System.Threading; using Couchbase.Lite; using Shouldly; -using Newtonsoft.Json; using Xunit; using Xunit.Abstractions; // ReSharper disable AccessToModifiedClosure @@ -2098,7 +2098,7 @@ public void TestTypesInDocumentToJSON() public void TestMutableDocWithJsonString() { var dic = PopulateDictData(); - var dicJson = JsonConvert.SerializeObject(dic, _jsonSerializerSettings); + var dicJson = JsonSerializer.Serialize(dic); using var md = new MutableDocument("doc1", dicJson); ValidateValuesInMutableDictFromJson(dic, md); } @@ -2119,7 +2119,7 @@ public void TestMutableDocumentSetJsonWithInvalidParam() //with array json string string[] arr = ["apple", "banana", "orange"]; - var jArr = JsonConvert.SerializeObject(arr); + var jArr = JsonSerializer.Serialize(arr); Should.Throw(() => md.SetJSON(jArr)); } @@ -2131,7 +2131,7 @@ public void TestCreateMutableDocWithInvalidStr() //with array json string string[] arr = ["apple", "banana", "orange"]; - var jArr = JsonConvert.SerializeObject(arr); + var jArr = JsonSerializer.Serialize(arr); Should.Throw(() => new MutableDocument("doc1", jArr)); } diff --git a/src/Couchbase.Lite.Tests.Shared/LogTest.cs b/src/Couchbase.Lite.Tests.Shared/LogTest.cs index 2f6e7e6e2..9135a950c 100644 --- a/src/Couchbase.Lite.Tests.Shared/LogTest.cs +++ b/src/Couchbase.Lite.Tests.Shared/LogTest.cs @@ -27,7 +27,7 @@ using Couchbase.Lite.Internal.Logging; using Couchbase.Lite.Logging; using Couchbase.Lite.Query; - +using Microsoft.Extensions.DependencyInjection; using Shouldly; using Xunit; using Xunit.Abstractions; @@ -36,13 +36,6 @@ namespace Test; public sealed class LogTest(ITestOutputHelper output) { -#if CBL_PLATFORM_DOTNET || CBL_PLATFORM_DOTNETFX - static LogTest() - { - Couchbase.Lite.Support.NetDesktop.CheckVersion(); - } -#endif - [Fact] public void TestDefaultLogFormat() { @@ -313,7 +306,7 @@ public void TestFileLogDisabledWarning() try { using var sw = new StringWriter(); Console.SetOut(sw); - var fakePath = Path.Combine(Service.GetRequiredInstance().DefaultDirectory(), "foo"); + var fakePath = Path.Combine(Service.Provider.GetRequiredService().DefaultDirectory(), "foo"); LogSinks.File = new FileLogSink(LogLevel.Info, fakePath); LogSinks.File = null; diff --git a/src/Couchbase.Lite.Tests.Shared/NotificationTest.cs b/src/Couchbase.Lite.Tests.Shared/NotificationTest.cs index 8994941b0..ff07fd275 100644 --- a/src/Couchbase.Lite.Tests.Shared/NotificationTest.cs +++ b/src/Couchbase.Lite.Tests.Shared/NotificationTest.cs @@ -19,10 +19,10 @@ using System.Collections.Generic; using System.Threading; using System.Threading.Tasks; +using System.Text.Json; using Couchbase.Lite; using Shouldly; -using Newtonsoft.Json; using Xunit; using Xunit.Abstractions; // ReSharper disable AccessToDisposedClosure @@ -457,7 +457,7 @@ private void CollectionChanged(object? sender, CollectionChangedEventArgs args) } WriteLine( - $"Expecting {_expectedDocumentChanges.Count} more changes ({JsonConvert.SerializeObject(_expectedDocumentChanges)})"); + $"Expecting {_expectedDocumentChanges.Count} more changes ({JsonSerializer.Serialize(_expectedDocumentChanges)})"); return _expectedDocumentChanges.Count == 0; } }); @@ -479,7 +479,7 @@ private void DocumentChanged(object? sender, DocumentChangedEventArgs args) _expectedDocumentChanges.Remove(args.DocumentID); WriteLine( - $"Expecting {_expectedDocumentChanges.Count} more changes ({JsonConvert.SerializeObject(_expectedDocumentChanges)})"); + $"Expecting {_expectedDocumentChanges.Count} more changes ({JsonSerializer.Serialize(_expectedDocumentChanges)})"); return _expectedDocumentChanges.Count == 0; } }); diff --git a/src/Couchbase.Lite.Tests.Shared/PredictiveQueryTest.cs b/src/Couchbase.Lite.Tests.Shared/PredictiveQueryTest.cs index 53742d866..6e64cc9c7 100644 --- a/src/Couchbase.Lite.Tests.Shared/PredictiveQueryTest.cs +++ b/src/Couchbase.Lite.Tests.Shared/PredictiveQueryTest.cs @@ -21,17 +21,17 @@ using System.Collections.Generic; using System.Linq; using System.Text; +using System.Text.Json; using Couchbase.Lite; using Couchbase.Lite.Enterprise.Query; +using Couchbase.Lite.Internal.Doc; using Couchbase.Lite.Query; using Shouldly; using LiteCore.Interop; -using Newtonsoft.Json; - using Xunit; using Xunit.Abstractions; // ReSharper disable AccessToDisposedClosure @@ -912,7 +912,7 @@ public void TestCosineDistance() private void TestDistanceFunction(IExpression distance, string testData) { - var tests = JsonConvert.DeserializeObject>>(testData); + var tests = DataOps.ParseTo>>(testData); tests.ShouldNotBeNull("because otherwise testData was invalid"); foreach (var t in tests) { diff --git a/src/Couchbase.Lite.Tests.Shared/QueryTest.cs b/src/Couchbase.Lite.Tests.Shared/QueryTest.cs index cd08ccfb1..d10513d68 100644 --- a/src/Couchbase.Lite.Tests.Shared/QueryTest.cs +++ b/src/Couchbase.Lite.Tests.Shared/QueryTest.cs @@ -21,6 +21,7 @@ using System.Diagnostics.CodeAnalysis; using System.Linq; using System.Text; +using System.Text.Json; using System.Text.RegularExpressions; using System.Threading; using System.Threading.Tasks; @@ -31,8 +32,6 @@ using Shouldly; -using Newtonsoft.Json; - using Test.Util; using Xunit; using Xunit.Abstractions; @@ -1420,7 +1419,7 @@ public void TestQuantifiedOperatorVariableKeyPath() var docID = $"doc-{i++}"; using var doc = new MutableDocument(docID); doc.SetValue("paths", cities); - var d = JsonConvert.SerializeObject(doc.ToDictionary()); + var d = JsonSerializer.Serialize(doc.ToDictionary()); WriteLine(d); DefaultCollection.Save(doc); } @@ -1772,7 +1771,7 @@ public void TestJoinWithArrayContains() .Where(typeExpr.EqualTo(Expression.String("bookmark")))) { var rs = q.Execute(); foreach (var r in rs) { - WriteLine(JsonConvert.SerializeObject(r.ToDictionary())); + WriteLine(JsonSerializer.Serialize(r.ToDictionary())); } } } @@ -1789,7 +1788,7 @@ public void TestQueryDeletedDocument() var rs = q.Execute(); var counter = 0; foreach (var r in rs) { - WriteLine($"Round 1: Result -> {JsonConvert.SerializeObject(r.ToDictionary())}"); + WriteLine($"Round 1: Result -> {JsonSerializer.Serialize(r.ToDictionary())}"); counter++; } @@ -1804,7 +1803,7 @@ public void TestQueryDeletedDocument() counter = 0; foreach (var r in rs) { r.GetString(0).ShouldBe(task2.Id); - WriteLine($"Round 2: Result -> {JsonConvert.SerializeObject(r.ToDictionary())}"); + WriteLine($"Round 2: Result -> {JsonSerializer.Serialize(r.ToDictionary())}"); counter++; } @@ -1828,7 +1827,7 @@ public void TestQueryWhereBooleanExpression() .Where(exprType.EqualTo(Expression.String("task")).And(exprComplete.EqualTo(Expression.Boolean(true))))) { var numRows = VerifyQuery(q, (_, row) => { - WriteLine($"res -> {JsonConvert.SerializeObject(row.ToDictionary())}"); + WriteLine($"res -> {JsonSerializer.Serialize(row.ToDictionary())}"); var dict = row.GetDictionary("_default"); dict.ShouldNotBeNull("because otherwise the query returned incorrect data"); dict.GetBoolean("complete").ShouldBeTrue(); @@ -1844,7 +1843,7 @@ public void TestQueryWhereBooleanExpression() .Where(exprType.EqualTo(Expression.String("task")).And(exprComplete.EqualTo(Expression.Boolean(false))))) { var numRows = VerifyQuery(q, (_, row) => { - WriteLine($"res -> {JsonConvert.SerializeObject(row.ToDictionary())}"); + WriteLine($"res -> {JsonSerializer.Serialize(row.ToDictionary())}"); var dict = row.GetDictionary("_default"); dict.ShouldNotBeNull("because otherwise the query returned incorrect data"); dict.GetBoolean("complete").ShouldBeFalse(); @@ -1860,7 +1859,7 @@ public void TestQueryWhereBooleanExpression() .Where(exprType.EqualTo(Expression.String("task")).And(exprComplete.EqualTo(Expression.Boolean(true))))) { var numRows = VerifyQuery(q, (_, row) => { - WriteLine($"res -> {JsonConvert.SerializeObject(row.ToDictionary())}"); + WriteLine($"res -> {JsonSerializer.Serialize(row.ToDictionary())}"); row.GetInt(0).ShouldBe(2); }); @@ -1872,7 +1871,7 @@ public void TestQueryWhereBooleanExpression() .Where(exprType.EqualTo(Expression.String("task")).And(exprComplete.EqualTo(Expression.Boolean(false))))) { var numRows = VerifyQuery(q, (_, row) => { - WriteLine($"res -> {JsonConvert.SerializeObject(row.ToDictionary())}"); + WriteLine($"res -> {JsonSerializer.Serialize(row.ToDictionary())}"); row.GetInt(0).ShouldBe(1); }); @@ -1912,10 +1911,10 @@ public void TestJoinAll() var mainAll2 = row.GetDictionary("main"); var secondAll1 = row.GetDictionary(1); var secondAll2 = row.GetDictionary("secondary"); - WriteLine($"mainAll1 -> {JsonConvert.SerializeObject(mainAll1)}"); - WriteLine($"mainAll2 -> {JsonConvert.SerializeObject(mainAll2)}"); - WriteLine($"secondAll1 -> {JsonConvert.SerializeObject(secondAll1)}"); - WriteLine($"secondAll2 -> {JsonConvert.SerializeObject(secondAll2)}"); + WriteLine($"mainAll1 -> {JsonSerializer.Serialize(mainAll1)}"); + WriteLine($"mainAll2 -> {JsonSerializer.Serialize(mainAll2)}"); + WriteLine($"secondAll1 -> {JsonSerializer.Serialize(secondAll1)}"); + WriteLine($"secondAll2 -> {JsonSerializer.Serialize(secondAll2)}"); mainAll1?.GetInt("number1").ShouldBe(42); mainAll2?.GetInt("number1").ShouldBe(42); @@ -2338,11 +2337,11 @@ public void TestQueryJoinAndSelectAll() var numRows = VerifyQuery(q, (n, row) => { if (n == 41) { - WriteLine($"41: {JsonConvert.SerializeObject(row.ToDictionary())}"); + WriteLine($"41: {JsonSerializer.Serialize(row.ToDictionary())}"); row.GetDictionary("main")?.GetInt("number2").ShouldBe(59); row.GetDictionary("secondary").ShouldBeNull(); } else if (n == 42) { - WriteLine($"42: {JsonConvert.SerializeObject(row.ToDictionary())}"); + WriteLine($"42: {JsonSerializer.Serialize(row.ToDictionary())}"); row.GetDictionary("main")?.GetInt("number2").ShouldBe(58); row.GetDictionary("secondary")?.GetInt("theone").ShouldBe(42); } diff --git a/src/Couchbase.Lite.Tests.Shared/ReplicationTest.cs b/src/Couchbase.Lite.Tests.Shared/ReplicationTest.cs index b8cb86238..ff49e6f2c 100644 --- a/src/Couchbase.Lite.Tests.Shared/ReplicationTest.cs +++ b/src/Couchbase.Lite.Tests.Shared/ReplicationTest.cs @@ -24,6 +24,7 @@ using System.Security.Cryptography.X509Certificates; using System.Threading; using System.Threading.Tasks; +using System.Text.Json; using Couchbase.Lite; using Couchbase.Lite.Sync; using Couchbase.Lite.Util; @@ -34,7 +35,6 @@ using LiteCore.Interop; -using Newtonsoft.Json; using System.Collections.Immutable; using System.Reflection; @@ -1267,7 +1267,7 @@ public void TestConflictResolverMergeDoc() } } - WriteLine($"Resulting merge: {JsonConvert.SerializeObject(updateDocDict)}"); + WriteLine($"Resulting merge: {JsonSerializer.Serialize(updateDocDict)}"); var doc1 = new MutableDocument(conflict.DocumentID); doc1.SetData(updateDocDict); diff --git a/src/Couchbase.Lite.Tests.Shared/ScopesCollections.ReplicationTest.cs b/src/Couchbase.Lite.Tests.Shared/ScopesCollections.ReplicationTest.cs index 04ad4ad91..fe89febb2 100644 --- a/src/Couchbase.Lite.Tests.Shared/ScopesCollections.ReplicationTest.cs +++ b/src/Couchbase.Lite.Tests.Shared/ScopesCollections.ReplicationTest.cs @@ -20,13 +20,13 @@ using System.Collections.Generic; using System.Linq; using System.Threading; +using System.Text.Json; using Couchbase.Lite; using Couchbase.Lite.Sync; using Shouldly; -using Newtonsoft.Json; using System.Collections.Immutable; using Test.Util; @@ -457,7 +457,7 @@ public void TestCollectionDocumentReplicationEvents() { r.AddDocumentReplicationListener((_, args) => { - _output.WriteLine($"{args.IsPush} {JsonConvert.SerializeObject(args.Documents.Select(x => $"{x.Flags} {x.CollectionName} {x.Id}"))}"); + _output.WriteLine($"{args.IsPush} {JsonSerializer.Serialize(args.Documents.Select(x => $"{x.Flags} {x.CollectionName} {x.Id}"))}"); pushWait.RunConditionalAssert(() => args.IsPush && args.Documents.Any(x => x.Flags.HasFlag(DocumentFlags.Deleted) && x is { CollectionName: "colA", Id: "doc" }) diff --git a/src/Couchbase.Lite.Tests.Shared/TestCase.cs b/src/Couchbase.Lite.Tests.Shared/TestCase.cs index 3fdb546de..45b9de05f 100644 --- a/src/Couchbase.Lite.Tests.Shared/TestCase.cs +++ b/src/Couchbase.Lite.Tests.Shared/TestCase.cs @@ -21,6 +21,7 @@ using System.IO; using System.Linq; using System.Threading; +using System.Text.Json; using Couchbase.Lite; using Couchbase.Lite.Logging; @@ -28,12 +29,10 @@ using Shouldly; -using Newtonsoft.Json; using Test.Util; using System.Reflection; using System.Text; -using Newtonsoft.Json.Linq; using Couchbase.Lite.Internal.Doc; using Xunit; using Xunit.Abstractions; @@ -225,7 +224,7 @@ internal void ValidateToJsonValues(string json, Dictionary dic) switch (i.Key) { case "blob": { - var b1JsonD = ((JObject) jDict[i.Key]).ToObject>(); + var b1JsonD = jDict[i.Key] as IDictionary; b1JsonD.ShouldNotBeNull("because otherwise ToObject failed"); var b2JsonD = ((Blob?) dic[i.Key])!.JsonRepresentation; @@ -234,7 +233,7 @@ internal void ValidateToJsonValues(string json, Dictionary dic) hasValue.ShouldBeTrue($"because otherwise b1JsonD is missing the key {kv.Key}"); gotValue!.ToString().ShouldBe(kv.Value?.ToString()); } - + var blob = new Blob(Db, b1JsonD); blob.ShouldBeEquivalentToFluent((Blob) dic[i.Key]!); break; @@ -249,11 +248,6 @@ internal void ValidateToJsonValues(string json, Dictionary dic) } } - internal readonly JsonSerializerSettings _jsonSerializerSettings = new() - { - DateParseHandling = DateParseHandling.DateTimeOffset - }; - internal static Blob ArrayTestBlob() => new Blob("text/plain", Encoding.UTF8.GetBytes("12345")); /// @@ -620,7 +614,7 @@ protected void LoadJSONResource(string resourceName, Database? db = null, Collec ReadFileByLines($"C/tests/data/{resourceName}.json", line => { var docID = $"doc-{++n:D3}"; - var json = JsonConvert.DeserializeObject>(line); + var json = DataOps.ParseTo>(line); json.ShouldNotBeNull("because otherwise the line failed to parse"); var doc = new MutableDocument(docID); doc.SetData(json!); diff --git a/src/Couchbase.Lite.Tests.Shared/TunesPerfTest.cs b/src/Couchbase.Lite.Tests.Shared/TunesPerfTest.cs index f7c4cab8f..cb1ef13fb 100644 --- a/src/Couchbase.Lite.Tests.Shared/TunesPerfTest.cs +++ b/src/Couchbase.Lite.Tests.Shared/TunesPerfTest.cs @@ -21,11 +21,11 @@ using System.IO; using System.Linq; using Couchbase.Lite; +using Couchbase.Lite.Internal.Doc; using Couchbase.Lite.Query; using Couchbase.Lite.Util; using Shouldly; -using Newtonsoft.Json; using Test.Util; using Xunit; using Xunit.Abstractions; @@ -62,14 +62,10 @@ public void TestPerformance() protected override void SetUp() { _tracks = new(); - var settings = new JsonSerializerSettings - { - DateParseHandling = DateParseHandling.DateTimeOffset - }; TestCase.ReadFileByLines("C/tests/data/iTunesMusicLibrary.json", line => { - _tracks.Add(JsonConvert.DeserializeObject>(line, settings)!); + _tracks.Add(DataOps.ParseTo>(line)!); return true; }); diff --git a/src/Couchbase.Lite/Couchbase.Lite.csproj b/src/Couchbase.Lite/Couchbase.Lite.csproj index 438a64e25..431a3463f 100644 --- a/src/Couchbase.Lite/Couchbase.Lite.csproj +++ b/src/Couchbase.Lite/Couchbase.Lite.csproj @@ -56,12 +56,11 @@ $(DefineConstants);NEEDS_LITECORE_LOAD + all runtime; build; native; contentfiles; analyzers; buildtransitive - - @@ -73,6 +72,7 @@ runtime; build; native; contentfiles; analyzers + diff --git a/src/LiteCore/src/LiteCore.Shared/Interop/Fleece.cs b/src/LiteCore/src/LiteCore.Shared/Interop/Fleece.cs index 5fdca5ab3..5597632f5 100644 --- a/src/LiteCore/src/LiteCore.Shared/Interop/Fleece.cs +++ b/src/LiteCore/src/LiteCore.Shared/Interop/Fleece.cs @@ -25,8 +25,9 @@ using System.Runtime.InteropServices; using System.Security; using System.Text; - +using System.Text.Json; using Couchbase.Lite; +using Couchbase.Lite.Internal.Doc; namespace LiteCore.Interop; @@ -533,6 +534,35 @@ public static void FLEncode(this object? obj, FLEncoder* enc) case SecureString ss: new NetworkCredential(string.Empty, ss).Password.FLEncode(enc); break; + case JsonElement jObj: + switch (jObj.ValueKind) { + case JsonValueKind.Array: + jObj.Deserialize(DataOps.SerializerOptions)!.FLEncode(); + break; + case JsonValueKind.Object: + jObj.Deserialize>(DataOps.SerializerOptions)!.FLEncode(); + break; + case JsonValueKind.Number: + if (jObj.TryGetInt64(out var l)) { + Native.FLEncoder_WriteInt(enc, l); + } else { + Native.FLEncoder_WriteDouble(enc, jObj.GetDouble()); + } + break; + case JsonValueKind.String: + Native.FLEncoder_WriteString(enc, jObj.GetString()); + break; + case JsonValueKind.True: + case JsonValueKind.False: + Native.FLEncoder_WriteBool(enc, jObj.GetBoolean()); + break; + case JsonValueKind.Null: + Native.FLEncoder_WriteNull(enc); + break; + default: + throw new ArgumentException("Invalid JsonElement type: " + jObj.ValueKind); + } + break; default: throw new ArgumentException($"Cannot encode {obj.GetType().FullName} to Fleece!"); }