Skip to content

Commit 00ae9c1

Browse files
committed
feat: Add org.json.JSONObject.from method (#1500)
1 parent b5a4b33 commit 00ae9c1

File tree

8 files changed

+219
-0
lines changed

8 files changed

+219
-0
lines changed

test-app/app/src/main/assets/app/mainpage.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,7 @@ require("./tests/testInterfaceImplementation");
5454
require("./tests/testRuntimeImplementedAPIs");
5555
require("./tests/testsInstanceOfOperator");
5656
require("./tests/testReleaseNativeCounterpart");
57+
require("./tests/testJSONObjects");
5758
require("./tests/kotlin/companions/testCompanionObjectsSupport");
5859
require("./tests/kotlin/properties/testPropertiesSupport");
5960
require("./tests/kotlin/delegation/testDelegationSupport");
Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
describe("Test JSONObject conversions", () => {
2+
it("org.json.JSONObject.from method is defined", () => {
3+
expect(typeof org.json.JSONObject.from).toBe("function");
4+
});
5+
6+
it("JSONObject.from takes at least one argument", () => {
7+
expect(() => org.json.JSONObject.from()).toThrowError();
8+
});
9+
10+
it("JSONObject.from with boolean", () => {
11+
let param = true;
12+
let actual = org.json.JSONObject.from(param);
13+
expect(actual).toBe(param);
14+
});
15+
16+
it("JSONObject.from with string", () => {
17+
let param = "some param";
18+
let actual = org.json.JSONObject.from(param);
19+
expect(actual).toBe(param);
20+
});
21+
22+
it("JSONObject.from with number", () => {
23+
let param = 123;
24+
let actual = org.json.JSONObject.from(param);
25+
expect(actual).toBe(param);
26+
});
27+
28+
it("JSONObject.from with date must serialize it to JSON using ISO8601", () => {
29+
let timestamp = 1570696661136;
30+
let param = new Date(timestamp);
31+
let actual = org.json.JSONObject.from(param);
32+
expect(actual).toBe("2019-10-10T08:37:41.136Z");
33+
});
34+
35+
it("JSONObject.from with object", () => {
36+
let param = {
37+
prop1: "prop1 value",
38+
prop2: 123,
39+
prop3: {
40+
prop4: "prop 4 value"
41+
}
42+
};
43+
let actual = org.json.JSONObject.from(param);
44+
expect(actual instanceof org.json.JSONObject).toBe(true);
45+
let actualStr = com.tns.tests.JSONObjectMethods.testWithObject(actual);
46+
expect(actualStr).toBe(`{"prop1":"prop1 value","prop2":123,"prop3":{"prop4":"prop 4 value"}}`);
47+
});
48+
49+
it("JSONObject.from with array", () => {
50+
let param = [{
51+
prop1: "item 1, prop1 value",
52+
prop2: 123
53+
}, {
54+
prop1: "item 2, prop1 value",
55+
prop2: 456
56+
}];
57+
let actual = org.json.JSONObject.from(param);
58+
expect(actual instanceof org.json.JSONArray).toBe(true);
59+
let actualStr = com.tns.tests.JSONObjectMethods.testWithArray(actual);
60+
expect(actualStr).toBe(`[{"prop1":"item 1, prop1 value","prop2":123},{"prop1":"item 2, prop1 value","prop2":456}]`);
61+
});
62+
});
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
package com.tns.tests;
2+
3+
import org.json.JSONArray;
4+
import org.json.JSONObject;
5+
6+
public class JSONObjectMethods {
7+
public static String testWithObject(JSONObject obj) {
8+
return obj.toString();
9+
}
10+
11+
public static String testWithArray(JSONArray obj) {
12+
return obj.toString();
13+
}
14+
}

test-app/runtime/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -149,6 +149,7 @@ add_library(
149149
src/main/cpp/JniSignatureParser.cpp
150150
src/main/cpp/JsArgConverter.cpp
151151
src/main/cpp/JsArgToArrayConverter.cpp
152+
src/main/cpp/JSONObjectHelper.cpp
152153
src/main/cpp/Logger.cpp
153154
src/main/cpp/ManualInstrumentation.cpp
154155
src/main/cpp/MetadataMethodInfo.cpp
Lines changed: 114 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,114 @@
1+
#include "NativeScriptException.h"
2+
#include "JSONObjectHelper.h"
3+
#include "ArgConverter.h"
4+
#include <sstream>
5+
#include <string>
6+
7+
using namespace v8;
8+
using namespace tns;
9+
10+
JSONObjectHelper::JSONObjectHelper()
11+
: m_objectManager(nullptr), m_serializeFunc(nullptr) {
12+
}
13+
14+
void JSONObjectHelper::CreateConvertFunctions(Isolate *isolate, const Local<Object> &global, ObjectManager* objectManager) {
15+
m_objectManager = objectManager;
16+
17+
m_serializeFunc = new Persistent<Function>(isolate, CreateSerializeFunc(isolate));
18+
19+
Local<Context> context = isolate->GetCurrentContext();
20+
21+
Local<External> extData = External::New(isolate, this);
22+
Local<Function> fromFunc = FunctionTemplate::New(isolate, ConvertCallbackStatic, extData)->GetFunction(context).ToLocalChecked();
23+
24+
Local<Function> jsonObjectFunc = global->Get(context, ArgConverter::ConvertToV8String(isolate, "org"))
25+
.ToLocalChecked().As<Object>()->Get(context, ArgConverter::ConvertToV8String(isolate, "json"))
26+
.ToLocalChecked().As<Object>()->Get(context, ArgConverter::ConvertToV8String(isolate, "JSONObject"))
27+
.ToLocalChecked().As<Function>();
28+
29+
jsonObjectFunc->Set(context, ArgConverter::ConvertToV8String(isolate, "from"), fromFunc);
30+
}
31+
32+
void JSONObjectHelper::ConvertCallbackStatic(const FunctionCallbackInfo<Value>& info) {
33+
try {
34+
Local<External> extData = info.Data().As<External>();
35+
auto thiz = reinterpret_cast<JSONObjectHelper*>(extData->Value());
36+
thiz->ConvertCallback(info);
37+
} catch (NativeScriptException& e) {
38+
e.ReThrowToV8();
39+
} catch (std::exception e) {
40+
std::stringstream ss;
41+
ss << "Error: c++ exception: " << e.what() << std::endl;
42+
NativeScriptException nsEx(ss.str());
43+
nsEx.ReThrowToV8();
44+
} catch (...) {
45+
NativeScriptException nsEx(std::string("Error: c++ exception!"));
46+
nsEx.ReThrowToV8();
47+
}
48+
}
49+
50+
void JSONObjectHelper::ConvertCallback(const FunctionCallbackInfo<Value>& info) {
51+
if (info.Length() < 1) {
52+
NativeScriptException nsEx(std::string("The \"from\" function expects one parameter"));
53+
nsEx.ReThrowToV8();
54+
return;
55+
}
56+
57+
Isolate* isolate = info.GetIsolate();
58+
Local<Context> context = isolate->GetCurrentContext();
59+
60+
Local<Function> serializeFunc = m_serializeFunc->Get(isolate);
61+
Local<Value> args[] = { info[0] };
62+
Local<Value> result;
63+
TryCatch tc(isolate);
64+
if (!serializeFunc->Call(context, Undefined(isolate), 1, args).ToLocal(&result)) {
65+
throw NativeScriptException(tc, "Error serializing to JSONObject");
66+
}
67+
68+
info.GetReturnValue().Set(result);
69+
}
70+
71+
Local<Function> JSONObjectHelper::CreateSerializeFunc(Isolate* isolate) {
72+
std::string source =
73+
"(() => function serialize(data) {"
74+
" let store;"
75+
" switch (typeof data) {"
76+
" case \"string\":"
77+
" case \"boolean\":"
78+
" case \"number\": {"
79+
" return data;"
80+
" }"
81+
" case \"object\": {"
82+
" if (!data) {"
83+
" return null;"
84+
" }"
85+
""
86+
" if (data instanceof Date) {"
87+
" return data.toJSON();"
88+
" }"
89+
""
90+
" if (Array.isArray(data)) {"
91+
" store = new org.json.JSONArray();"
92+
" data.forEach((item) => store.put(serialize(item)));"
93+
" return store;"
94+
" }"
95+
""
96+
" store = new org.json.JSONObject();"
97+
" Object.keys(data).forEach((key) => store.put(key, serialize(data[key])));"
98+
" return store;"
99+
" }"
100+
" default:"
101+
" return null;"
102+
" }"
103+
"})()";
104+
105+
Local<Context> context = isolate->GetCurrentContext();
106+
107+
Local<Script> script = Script::Compile(context, ArgConverter::ConvertToV8String(isolate, source)).ToLocalChecked();
108+
109+
Local<Value> result = script->Run(context).ToLocalChecked();
110+
111+
Local<Function> serializeFunc = result.As<Function>();
112+
113+
return serializeFunc;
114+
}
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
#ifndef JSONOBJECTHELPER_H_
2+
#define JSONOBJECTHELPER_H_
3+
4+
#include "v8.h"
5+
#include "ObjectManager.h"
6+
7+
namespace tns {
8+
9+
class JSONObjectHelper {
10+
public:
11+
JSONObjectHelper();
12+
void CreateConvertFunctions(v8::Isolate* isolate, const v8::Local<v8::Object>& global, ObjectManager* objectManager);
13+
private:
14+
ObjectManager* m_objectManager;
15+
v8::Persistent<v8::Function>* m_serializeFunc;
16+
17+
v8::Local<v8::Function> CreateSerializeFunc(v8::Isolate* isolate);
18+
void ConvertCallback(const v8::FunctionCallbackInfo<v8::Value>& info);
19+
static void ConvertCallbackStatic(const v8::FunctionCallbackInfo<v8::Value>& info);
20+
};
21+
22+
}
23+
24+
#endif //JSONOBJECTHELPER_H_

test-app/runtime/src/main/cpp/Runtime.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -697,6 +697,7 @@ Isolate* Runtime::PrepareV8Runtime(const string& filesPath, const string& native
697697
ArrayHelper::Init(context);
698698

699699
m_arrayBufferHelper.CreateConvertFunctions(isolate, global, m_objectManager);
700+
m_jsonObjectHelper.CreateConvertFunctions(isolate, global, m_objectManager);
700701

701702
s_mainThreadInitialized = true;
702703

test-app/runtime/src/main/cpp/Runtime.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
#include "SimpleAllocator.h"
88
#include "WeakRef.h"
99
#include "ArrayBufferHelper.h"
10+
#include "JSONObjectHelper.h"
1011
#include "Profiler.h"
1112
#include "ModuleInternal.h"
1213
#include "File.h"
@@ -77,6 +78,7 @@ class Runtime {
7778
ModuleInternal m_module;
7879

7980
ArrayBufferHelper m_arrayBufferHelper;
81+
JSONObjectHelper m_jsonObjectHelper;
8082

8183
WeakRef m_weakRef;
8284

0 commit comments

Comments
 (0)