mirror of
https://github.com/qmlnet/qmlnet.git
synced 2026-05-22 06:05:26 -06:00
Added support for serializing .NET objects to strings.
This commit is contained in:
parent
cae6c8e4a0
commit
5a7bd17c21
13 changed files with 203 additions and 16 deletions
|
|
@ -15,6 +15,7 @@ void Heap::NetObject::init() {
|
|||
o->defineDefaultProperty(QStringLiteral("gcCollect"), QV4::NetObject::method_gccollect);
|
||||
o->defineDefaultProperty(QStringLiteral("await"), QV4::NetObject::method_await);
|
||||
o->defineDefaultProperty(QStringLiteral("cancelTokenSource"), QV4::NetObject::method_cancelTokenSource);
|
||||
o->defineDefaultProperty(QStringLiteral("serialize"), QV4::NetObject::method_serialize);
|
||||
}
|
||||
|
||||
#if QT_VERSION < QT_VERSION_CHECK(5, 11, 0)
|
||||
|
|
@ -92,6 +93,34 @@ void NetObject::method_cancelTokenSource(const BuiltinFunction *, Scope &scope,
|
|||
scope.result = QV4::QObjectWrapper::wrap(scope.engine, netValue);
|
||||
}
|
||||
|
||||
void NetObject::method_serialize(const BuiltinFunction *, Scope &scope, CallData *callData)
|
||||
{
|
||||
qDebug("TESTdd");
|
||||
|
||||
if(callData->argc != 1) {
|
||||
THROW_GENERIC_ERROR("Net.serialize(): Missing instance parameter");
|
||||
}
|
||||
|
||||
ScopedValue instance(scope, callData->args[0]);
|
||||
if(instance->isNullOrUndefined()) {
|
||||
THROW_GENERIC_ERROR("Net.serialize(): Instance parameter must not be null or undefined");
|
||||
}
|
||||
|
||||
QJSValue instanceJsValue(scope.engine, instance->asReturnedValue());
|
||||
QSharedPointer<NetVariant> value = NetVariant::fromQJSValue(instanceJsValue);
|
||||
if(value->getVariantType() != NetVariantTypeEnum_Object) {
|
||||
THROW_GENERIC_ERROR("Net.serialize(): Parameter is not a .NET object");
|
||||
}
|
||||
|
||||
QSharedPointer<NetVariant> result = QSharedPointer<NetVariant>(new NetVariant());
|
||||
bool serializationResult = serializeNetToString(value->getNetReference(), result);
|
||||
if(!serializationResult) {
|
||||
THROW_GENERIC_ERROR("Net.serialize(): Could not serialize object.");
|
||||
}
|
||||
|
||||
scope.result = scope.engine->newString(result->getString());
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
ReturnedValue NetObject::method_gccollect(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc) {
|
||||
|
|
@ -185,4 +214,28 @@ ReturnedValue NetObject::method_cancelTokenSource(const FunctionObject *b, const
|
|||
return QV4::QObjectWrapper::wrap(scope.engine, netValue);
|
||||
}
|
||||
|
||||
ReturnedValue NetObject::method_serialize(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc)
|
||||
{
|
||||
QV4::Scope scope(b);
|
||||
if(argc != 1) {
|
||||
THROW_GENERIC_ERROR("Net.serialize(): Missing instance parameter");
|
||||
}
|
||||
QV4::ScopedValue instance(scope, argv[0]);
|
||||
if(instance->isNullOrUndefined()) {
|
||||
THROW_GENERIC_ERROR("Net.serialize(): Instance parameter must not be null or undefined");
|
||||
}
|
||||
QJSValue instanceJsValue(scope.engine, instance->asReturnedValue());
|
||||
QSharedPointer<NetVariant> value = NetVariant::fromQJSValue(instanceJsValue);
|
||||
if(value->getVariantType() != NetVariantTypeEnum_Object) {
|
||||
THROW_GENERIC_ERROR("Net.serialize(): Parameter is not a .NET object");
|
||||
}
|
||||
QSharedPointer<NetVariant> result = QSharedPointer<NetVariant>(new NetVariant());
|
||||
bool serializationResult = serializeNetToString(value->getNetReference(), result);
|
||||
if(!serializationResult) {
|
||||
THROW_GENERIC_ERROR("Net.serialize(): Could not serialize object.");
|
||||
}
|
||||
|
||||
return Encode(scope.engine->newString(result->getString()));
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -20,10 +20,12 @@ struct NetObject : Object
|
|||
static void method_gccollect(const BuiltinFunction *, Scope &scope, CallData *callData);
|
||||
static void method_await(const BuiltinFunction *, Scope &scope, CallData *callData);
|
||||
static void method_cancelTokenSource(const BuiltinFunction *, Scope &scope, CallData *callData);
|
||||
static void method_serialize(const BuiltinFunction *, Scope &scope, CallData *callData);
|
||||
#else
|
||||
static ReturnedValue method_gccollect(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc);
|
||||
static ReturnedValue method_await(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc);
|
||||
static ReturnedValue method_cancelTokenSource(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc);
|
||||
static ReturnedValue method_serialize(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc);
|
||||
#endif
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -12,6 +12,7 @@ typedef void (*invokeMethodCb)(NetMethodInfoContainer* method, NetReferenceConta
|
|||
typedef void (*gcCollectCb)(int maxGeneration);
|
||||
typedef bool (*raiseNetSignalsCb)(NetReferenceContainer* target, LPWCSTR signalName, NetVariantListContainer* parameters);
|
||||
typedef void (*awaitTaskCb)(NetReferenceContainer* target, NetJSValueContainer* successCallback, NetJSValueContainer* failureCallback);
|
||||
typedef bool (*serializeNetToStringCb)(NetReferenceContainer* instance, NetVariantContainer* result);
|
||||
|
||||
struct Q_DECL_EXPORT NetTypeInfoManagerCallbacks {
|
||||
isTypeValidCb isTypeValid;
|
||||
|
|
@ -26,6 +27,7 @@ struct Q_DECL_EXPORT NetTypeInfoManagerCallbacks {
|
|||
gcCollectCb gcCollect;
|
||||
raiseNetSignalsCb raiseNetSignals;
|
||||
awaitTaskCb awaitTask;
|
||||
serializeNetToStringCb serializeNetToString;
|
||||
};
|
||||
|
||||
static NetTypeInfoManagerCallbacks sharedCallbacks;
|
||||
|
|
@ -136,6 +138,12 @@ void awaitTask(QSharedPointer<NetReference> target, QSharedPointer<NetJSValue> s
|
|||
sharedCallbacks.awaitTask(targetContainer, sucessCallbackContainer, failureCallbackContainer);
|
||||
}
|
||||
|
||||
bool serializeNetToString(QSharedPointer<NetReference> instance, QSharedPointer<NetVariant> result)
|
||||
{
|
||||
return sharedCallbacks.serializeNetToString(new NetReferenceContainer{instance},
|
||||
new NetVariantContainer{result});
|
||||
}
|
||||
|
||||
extern "C" {
|
||||
|
||||
Q_DECL_EXPORT void type_info_callbacks_registerCallbacks(NetTypeInfoManagerCallbacks* callbacks) {
|
||||
|
|
|
|||
|
|
@ -35,4 +35,6 @@ bool raiseNetSignals(QSharedPointer<NetReference> target, QString signalName, QS
|
|||
|
||||
void awaitTask(QSharedPointer<NetReference> target, QSharedPointer<NetJSValue> successCallback, QSharedPointer<NetJSValue> failureCallback);
|
||||
|
||||
bool serializeNetToString(QSharedPointer<NetReference> instance, QSharedPointer<NetVariant> result);
|
||||
|
||||
#endif // NET_TYPE_INFO_MANAGER_H
|
||||
|
|
|
|||
|
|
@ -9,19 +9,23 @@ namespace Qml.Net.Sandbox
|
|||
{
|
||||
class Program
|
||||
{
|
||||
[Signal("testSignal", NetVariantType.String)]
|
||||
public class TestQmlImport
|
||||
{
|
||||
public void Test(INetJsValue jsValue)
|
||||
public TestObject GetObject()
|
||||
{
|
||||
Console.WriteLine("In net method, invoking callback");
|
||||
jsValue.Call();
|
||||
return new TestObject
|
||||
{
|
||||
Prop1 = "test1",
|
||||
Prop2 = 3
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
public void AnotherMethod()
|
||||
{
|
||||
Console.WriteLine("Another method");
|
||||
}
|
||||
public class TestObject
|
||||
{
|
||||
public string Prop1 { get; set; }
|
||||
|
||||
public int Prop2 { get; set; }
|
||||
}
|
||||
|
||||
static int Main(string[] args)
|
||||
|
|
|
|||
|
|
@ -10,14 +10,16 @@ ApplicationWindow {
|
|||
title: qsTr("Hello World")
|
||||
Item {
|
||||
Timer {
|
||||
interval: 5000; running: true; repeat: true
|
||||
interval: 1000; running: true; repeat: true
|
||||
onTriggered: {
|
||||
console.log('calling function with callback')
|
||||
var task = test.Test(function() {
|
||||
console.log('in callback, going to call AnotherMethod')
|
||||
test.AnotherMethod()
|
||||
console.log('done calling AnotherMethod')
|
||||
})
|
||||
var netObject = test.getObject()
|
||||
var toJson = Net.serialize(netObject)
|
||||
console.log(toJson)
|
||||
console.log(typeof toJson)
|
||||
toJson = JSON.parse(toJson)
|
||||
console.log(toJson)
|
||||
console.log(typeof toJson)
|
||||
console.log(toJson.prop1)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
46
src/net/Qml.Net.Tests/Qml/SerializationTests.cs
Normal file
46
src/net/Qml.Net.Tests/Qml/SerializationTests.cs
Normal file
|
|
@ -0,0 +1,46 @@
|
|||
using Moq;
|
||||
using Xunit;
|
||||
|
||||
namespace Qml.Net.Tests.Qml
|
||||
{
|
||||
public class SerializationTests : BaseQmlTests<SerializationTests.SerializationTestsQml>
|
||||
{
|
||||
public class SerializationTestsQml
|
||||
{
|
||||
public virtual JsonObject GetJsonObject()
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
public virtual string Result { get; set; }
|
||||
}
|
||||
|
||||
public class JsonObject
|
||||
{
|
||||
public string Prop1 { get; set; }
|
||||
|
||||
public int Prop2 { get; set; }
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Can_serialize_object()
|
||||
{
|
||||
var jsonObject = new JsonObject();
|
||||
jsonObject.Prop1 = "test1";
|
||||
jsonObject.Prop2 = 3;
|
||||
var jsonObjectSerialized = Serializer.Current.Serialize(jsonObject);
|
||||
Mock.Setup(x => x.GetJsonObject()).Returns(jsonObject);
|
||||
Mock.SetupSet(x => x.Result = jsonObjectSerialized);
|
||||
|
||||
RunQmlTest(
|
||||
"test",
|
||||
@"
|
||||
var jsonObject = test.getJsonObject()
|
||||
test.result = Net.serialize(jsonObject)
|
||||
");
|
||||
|
||||
Mock.Verify(x => x.GetJsonObject(), Times.Once);
|
||||
Mock.VerifySet(x => x.Result = jsonObjectSerialized, Times.Once);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -391,6 +391,25 @@ namespace Qml.Net.Internal
|
|||
}
|
||||
}
|
||||
|
||||
public bool Serialize(IntPtr i, IntPtr r)
|
||||
{
|
||||
using (var instance = new NetReference(i))
|
||||
using (var result = new NetVariant(r))
|
||||
{
|
||||
try
|
||||
{
|
||||
result.String = Serializer.Current.Serialize(instance.Instance);
|
||||
return true;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
// TODO: Propagate this error to the user.
|
||||
Console.Error.WriteLine($"Error serializing .NET object: {ex.Message}");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private NetVariantType GetPrefVariantType(Type typeInfo)
|
||||
{
|
||||
if (typeInfo == typeof(bool))
|
||||
|
|
|
|||
|
|
@ -20,6 +20,7 @@ namespace Qml.Net.Internal.Types
|
|||
public IntPtr GCCollect;
|
||||
public IntPtr RaseNetSignals;
|
||||
public IntPtr AwaitTask;
|
||||
public IntPtr Serialize;
|
||||
}
|
||||
|
||||
internal interface ICallbacksIterop
|
||||
|
|
@ -74,6 +75,8 @@ namespace Qml.Net.Internal.Types
|
|||
bool RaiseNetSignals(IntPtr target, string signalName, IntPtr parameters);
|
||||
|
||||
Task AwaitTask(IntPtr target, IntPtr succesCallback, IntPtr failureCallback);
|
||||
|
||||
bool Serialize(IntPtr instance, IntPtr result);
|
||||
}
|
||||
|
||||
internal class CallbacksImpl
|
||||
|
|
@ -91,6 +94,7 @@ namespace Qml.Net.Internal.Types
|
|||
GCCollectDelegate _gcCollectDelegate;
|
||||
RaiseNetSignalsDelegate _raiseNetSignalsDelegate;
|
||||
AwaitTaskDelegate _awaitTaskDelegate;
|
||||
SerializeDelegate _serializeDelegate;
|
||||
|
||||
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
|
||||
delegate bool IsTypeValidDelegate([MarshalAs(UnmanagedType.LPWStr)]string typeName);
|
||||
|
|
@ -128,6 +132,9 @@ namespace Qml.Net.Internal.Types
|
|||
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
|
||||
delegate void AwaitTaskDelegate(IntPtr target, IntPtr successCallback, IntPtr failureCallback);
|
||||
|
||||
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
|
||||
delegate bool SerializeDelegate(IntPtr instance, IntPtr result);
|
||||
|
||||
public CallbacksImpl(ICallbacks callbacks)
|
||||
{
|
||||
_callbacks = callbacks;
|
||||
|
|
@ -167,6 +174,9 @@ namespace Qml.Net.Internal.Types
|
|||
|
||||
_awaitTaskDelegate = AwaitTask;
|
||||
GCHandle.Alloc(_awaitTaskDelegate);
|
||||
|
||||
_serializeDelegate = Serialize;
|
||||
GCHandle.Alloc(_serializeDelegate);
|
||||
}
|
||||
|
||||
private bool IsTypeValid(string typeName)
|
||||
|
|
@ -231,6 +241,11 @@ namespace Qml.Net.Internal.Types
|
|||
_callbacks.AwaitTask(target, succesCallback, failureCallback);
|
||||
}
|
||||
|
||||
private bool Serialize(IntPtr instance, IntPtr result)
|
||||
{
|
||||
return _callbacks.Serialize(instance, result);
|
||||
}
|
||||
|
||||
public Callbacks Callbacks()
|
||||
{
|
||||
return new Callbacks
|
||||
|
|
@ -246,7 +261,8 @@ namespace Qml.Net.Internal.Types
|
|||
InvokeMethod = Marshal.GetFunctionPointerForDelegate(_invokeMethodDelegate),
|
||||
GCCollect = Marshal.GetFunctionPointerForDelegate(_gcCollectDelegate),
|
||||
RaseNetSignals = Marshal.GetFunctionPointerForDelegate(_raiseNetSignalsDelegate),
|
||||
AwaitTask = Marshal.GetFunctionPointerForDelegate(_awaitTaskDelegate)
|
||||
AwaitTask = Marshal.GetFunctionPointerForDelegate(_awaitTaskDelegate),
|
||||
Serialize = Marshal.GetFunctionPointerForDelegate(_serializeDelegate)
|
||||
};
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,5 +4,6 @@
|
|||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<PackageReference Include="AdvancedDLSupport" Version="2.1.0" />
|
||||
<PackageReference Include="Newtonsoft.Json" Version="11.0.2" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
7
src/net/Qml.Net/Serialization/ISerializer.cs
Normal file
7
src/net/Qml.Net/Serialization/ISerializer.cs
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
namespace Qml.Net.Serialization
|
||||
{
|
||||
public interface ISerializer
|
||||
{
|
||||
string Serialize(object value);
|
||||
}
|
||||
}
|
||||
18
src/net/Qml.Net/Serialization/NewtonSerializer.cs
Normal file
18
src/net/Qml.Net/Serialization/NewtonSerializer.cs
Normal file
|
|
@ -0,0 +1,18 @@
|
|||
using Newtonsoft.Json;
|
||||
using Newtonsoft.Json.Serialization;
|
||||
|
||||
namespace Qml.Net.Serialization
|
||||
{
|
||||
public class NewtonSerializer : ISerializer
|
||||
{
|
||||
private JsonSerializerSettings _defaultSerializerSettings = new JsonSerializerSettings
|
||||
{
|
||||
ContractResolver = new CamelCasePropertyNamesContractResolver()
|
||||
};
|
||||
|
||||
public string Serialize(object value)
|
||||
{
|
||||
return JsonConvert.SerializeObject(value, _defaultSerializerSettings);
|
||||
}
|
||||
}
|
||||
}
|
||||
9
src/net/Qml.Net/Serializer.cs
Normal file
9
src/net/Qml.Net/Serializer.cs
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
using Qml.Net.Serialization;
|
||||
|
||||
namespace Qml.Net
|
||||
{
|
||||
public class Serializer
|
||||
{
|
||||
public static ISerializer Current = new NewtonSerializer();
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue