nodenetcdf 4.9.3 → 4.9.32
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/TYPESCRIPT.md +211 -0
- package/index.d.ts +389 -0
- package/package.json +7 -3
- package/scripts/test-types.js +15 -0
- package/src/Attribute.cpp +54 -0
- package/src/Attribute.h +1 -0
- package/src/Dimension.cpp +36 -0
- package/src/Dimension.h +1 -0
- package/src/File.cpp +14 -0
- package/src/File.h +1 -0
- package/src/Group.cpp +129 -14
- package/src/Group.h +1 -0
- package/src/Variable.cpp +62 -2
- package/src/Variable.h +1 -0
- package/test/attribute.js +62 -0
- package/test/json.js +174 -0
- package/test/typescript-test.ts +87 -0
package/src/Dimension.cpp
CHANGED
|
@@ -25,6 +25,7 @@ void Dimension::Init(v8::Local<v8::Object> exports)
|
|
|
25
25
|
tpl->SetClassName(v8::String::NewFromUtf8(isolate, "Dimension", v8::NewStringType::kNormal).ToLocalChecked());
|
|
26
26
|
tpl->InstanceTemplate()->SetInternalFieldCount(1);
|
|
27
27
|
NODE_SET_PROTOTYPE_METHOD(tpl, "inspect", Dimension::Inspect);
|
|
28
|
+
NODE_SET_PROTOTYPE_METHOD(tpl, "toJSON", Dimension::ToJSON);
|
|
28
29
|
tpl->InstanceTemplate()->SetAccessor(
|
|
29
30
|
v8::String::NewFromUtf8(isolate, "id", v8::NewStringType::kNormal).ToLocalChecked(), Dimension::GetId);
|
|
30
31
|
tpl->InstanceTemplate()->SetAccessor(
|
|
@@ -102,4 +103,39 @@ void Dimension::Inspect(const v8::FunctionCallbackInfo<v8::Value> &args)
|
|
|
102
103
|
args.GetReturnValue().Set(
|
|
103
104
|
v8::String::NewFromUtf8(isolate, "[object Dimension]", v8::NewStringType::kNormal).ToLocalChecked());
|
|
104
105
|
}
|
|
106
|
+
|
|
107
|
+
void Dimension::ToJSON(const v8::FunctionCallbackInfo<v8::Value> &args)
|
|
108
|
+
{
|
|
109
|
+
v8::Isolate *isolate = args.GetIsolate();
|
|
110
|
+
v8::Local<v8::Context> context = isolate->GetCurrentContext();
|
|
111
|
+
const auto *obj = node::ObjectWrap::Unwrap<Dimension>(args.Holder());
|
|
112
|
+
|
|
113
|
+
// Use internalized strings for better performance
|
|
114
|
+
v8::Local<v8::String> id_str = v8::String::NewFromUtf8Literal(isolate, "id");
|
|
115
|
+
v8::Local<v8::String> name_str = v8::String::NewFromUtf8Literal(isolate, "name");
|
|
116
|
+
v8::Local<v8::String> length_str = v8::String::NewFromUtf8Literal(isolate, "length");
|
|
117
|
+
|
|
118
|
+
v8::Local<v8::Object> json = v8::Object::New(isolate);
|
|
119
|
+
|
|
120
|
+
// Add id
|
|
121
|
+
(void)json->CreateDataProperty(context, id_str, v8::Integer::New(isolate, obj->id));
|
|
122
|
+
|
|
123
|
+
// Add name
|
|
124
|
+
std::array<char, NC_MAX_NAME + 1> name{};
|
|
125
|
+
if (obj->get_name(name.data()))
|
|
126
|
+
{
|
|
127
|
+
(void)json->CreateDataProperty(context, name_str,
|
|
128
|
+
v8::String::NewFromUtf8(isolate, name.data(), v8::NewStringType::kInternalized).ToLocalChecked());
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
// Add length
|
|
132
|
+
size_t len = 0;
|
|
133
|
+
const int retval = nc_inq_dimlen(obj->parent_id, obj->id, &len);
|
|
134
|
+
if (retval == NC_NOERR)
|
|
135
|
+
{
|
|
136
|
+
(void)json->CreateDataProperty(context, length_str, v8::Integer::New(isolate, static_cast<int32_t>(len)));
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
args.GetReturnValue().Set(json);
|
|
140
|
+
}
|
|
105
141
|
} // namespace nodenetcdfjs
|
package/src/Dimension.h
CHANGED
|
@@ -30,6 +30,7 @@ class Dimension : public node::ObjectWrap
|
|
|
30
30
|
static void SetName(v8::Local<v8::String> property, v8::Local<v8::Value> val,
|
|
31
31
|
const v8::PropertyCallbackInfo<void> &info);
|
|
32
32
|
static void Inspect(const v8::FunctionCallbackInfo<v8::Value> &args);
|
|
33
|
+
static void ToJSON(const v8::FunctionCallbackInfo<v8::Value> &args);
|
|
33
34
|
|
|
34
35
|
int id{-1};
|
|
35
36
|
int parent_id{-1};
|
package/src/File.cpp
CHANGED
|
@@ -38,6 +38,7 @@ void File::Init(v8::Local<v8::Object> exports)
|
|
|
38
38
|
NODE_SET_PROTOTYPE_METHOD(tpl, "sync", File::Sync);
|
|
39
39
|
NODE_SET_PROTOTYPE_METHOD(tpl, "close", File::Close);
|
|
40
40
|
NODE_SET_PROTOTYPE_METHOD(tpl, "inspect", File::Inspect);
|
|
41
|
+
NODE_SET_PROTOTYPE_METHOD(tpl, "toJSON", File::ToJSON);
|
|
41
42
|
constructor.Reset(isolate, tpl->GetFunction(isolate->GetCurrentContext()).ToLocalChecked());
|
|
42
43
|
exports->Set(isolate->GetCurrentContext(),
|
|
43
44
|
v8::String::NewFromUtf8(isolate, "File", v8::NewStringType::kNormal).ToLocalChecked(),
|
|
@@ -167,4 +168,17 @@ void File::Inspect(const v8::FunctionCallbackInfo<v8::Value> &args)
|
|
|
167
168
|
args.GetReturnValue().Set(
|
|
168
169
|
v8::String::NewFromUtf8(isolate, "[object File]", v8::NewStringType::kNormal).ToLocalChecked());
|
|
169
170
|
}
|
|
171
|
+
|
|
172
|
+
void File::ToJSON(const v8::FunctionCallbackInfo<v8::Value> &args)
|
|
173
|
+
{
|
|
174
|
+
v8::Isolate *isolate = args.GetIsolate();
|
|
175
|
+
v8::Local<v8::Context> context = isolate->GetCurrentContext();
|
|
176
|
+
|
|
177
|
+
// Get the root group and serialize it
|
|
178
|
+
v8::Local<v8::String> rootProp = v8::String::NewFromUtf8(isolate, "root", v8::NewStringType::kNormal).ToLocalChecked();
|
|
179
|
+
v8::Local<v8::Value> root = args.Holder()->Get(context, rootProp).ToLocalChecked();
|
|
180
|
+
|
|
181
|
+
// The root group's toJSON will automatically serialize all children recursively
|
|
182
|
+
args.GetReturnValue().Set(root);
|
|
183
|
+
}
|
|
170
184
|
} // namespace nodenetcdfjs
|
package/src/File.h
CHANGED
|
@@ -30,6 +30,7 @@ class File : public node::ObjectWrap
|
|
|
30
30
|
static void Close(const v8::FunctionCallbackInfo<v8::Value> &args);
|
|
31
31
|
static void Sync(const v8::FunctionCallbackInfo<v8::Value> &args);
|
|
32
32
|
static void Inspect(const v8::FunctionCallbackInfo<v8::Value> &args);
|
|
33
|
+
static void ToJSON(const v8::FunctionCallbackInfo<v8::Value> &args);
|
|
33
34
|
|
|
34
35
|
static v8::Persistent<v8::Function> constructor;
|
|
35
36
|
|
package/src/Group.cpp
CHANGED
|
@@ -31,6 +31,7 @@ void Group::Init(v8::Local<v8::Object> exports)
|
|
|
31
31
|
NODE_SET_PROTOTYPE_METHOD(tpl, "addSubgroup", Group::AddSubgroup);
|
|
32
32
|
NODE_SET_PROTOTYPE_METHOD(tpl, "addAttribute", Group::AddAttribute);
|
|
33
33
|
NODE_SET_PROTOTYPE_METHOD(tpl, "inspect", Group::Inspect);
|
|
34
|
+
NODE_SET_PROTOTYPE_METHOD(tpl, "toJSON", Group::ToJSON);
|
|
34
35
|
tpl->InstanceTemplate()->SetAccessor(
|
|
35
36
|
v8::String::NewFromUtf8(isolate, "id", v8::NewStringType::kNormal).ToLocalChecked(), Group::GetId);
|
|
36
37
|
tpl->InstanceTemplate()->SetAccessor(
|
|
@@ -244,6 +245,7 @@ void Group::GetId(v8::Local<v8::String> property, const v8::PropertyCallbackInfo
|
|
|
244
245
|
void Group::GetVariables(v8::Local<v8::String> property, const v8::PropertyCallbackInfo<v8::Value> &info)
|
|
245
246
|
{
|
|
246
247
|
v8::Isolate *isolate = info.GetIsolate();
|
|
248
|
+
v8::Local<v8::Context> context = isolate->GetCurrentContext();
|
|
247
249
|
const auto *obj = node::ObjectWrap::Unwrap<Group>(info.Holder());
|
|
248
250
|
|
|
249
251
|
int nvars = 0;
|
|
@@ -270,8 +272,8 @@ void Group::GetVariables(v8::Local<v8::String> property, const v8::PropertyCallb
|
|
|
270
272
|
auto *v = new Variable(var_ids[i], obj->id);
|
|
271
273
|
if (v->get_name(name.data()))
|
|
272
274
|
{
|
|
273
|
-
result->
|
|
274
|
-
v8::String::NewFromUtf8(isolate, name.data(), v8::NewStringType::
|
|
275
|
+
(void)result->CreateDataProperty(context,
|
|
276
|
+
v8::String::NewFromUtf8(isolate, name.data(), v8::NewStringType::kInternalized).ToLocalChecked(),
|
|
275
277
|
v->handle());
|
|
276
278
|
}
|
|
277
279
|
else
|
|
@@ -285,6 +287,7 @@ void Group::GetVariables(v8::Local<v8::String> property, const v8::PropertyCallb
|
|
|
285
287
|
void Group::GetDimensions(v8::Local<v8::String> property, const v8::PropertyCallbackInfo<v8::Value> &info)
|
|
286
288
|
{
|
|
287
289
|
v8::Isolate *isolate = info.GetIsolate();
|
|
290
|
+
v8::Local<v8::Context> context = isolate->GetCurrentContext();
|
|
288
291
|
const auto *obj = node::ObjectWrap::Unwrap<Group>(info.Holder());
|
|
289
292
|
|
|
290
293
|
int ndims = 0;
|
|
@@ -311,10 +314,8 @@ void Group::GetDimensions(v8::Local<v8::String> property, const v8::PropertyCall
|
|
|
311
314
|
auto *d = new Dimension(dim_ids[i], obj->id);
|
|
312
315
|
if (d->get_name(name.data()))
|
|
313
316
|
{
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
isolate->GetCurrentContext(),
|
|
317
|
-
v8::String::NewFromUtf8(isolate, name.data(), v8::NewStringType::kNormal).ToLocalChecked(),
|
|
317
|
+
(void)result->CreateDataProperty(context,
|
|
318
|
+
v8::String::NewFromUtf8(isolate, name.data(), v8::NewStringType::kInternalized).ToLocalChecked(),
|
|
318
319
|
d->handle());
|
|
319
320
|
}
|
|
320
321
|
else
|
|
@@ -326,6 +327,7 @@ void Group::GetDimensions(v8::Local<v8::String> property, const v8::PropertyCall
|
|
|
326
327
|
void Group::GetUnlimited(v8::Local<v8::String> property, const v8::PropertyCallbackInfo<v8::Value> &info)
|
|
327
328
|
{
|
|
328
329
|
v8::Isolate *isolate = info.GetIsolate();
|
|
330
|
+
v8::Local<v8::Context> context = isolate->GetCurrentContext();
|
|
329
331
|
const auto *obj = node::ObjectWrap::Unwrap<Group>(info.Holder());
|
|
330
332
|
|
|
331
333
|
int ndims = 0;
|
|
@@ -352,9 +354,8 @@ void Group::GetUnlimited(v8::Local<v8::String> property, const v8::PropertyCallb
|
|
|
352
354
|
auto *d = new Dimension(dim_ids[i], obj->id);
|
|
353
355
|
if (d->get_name(name.data()))
|
|
354
356
|
{
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
v8::String::NewFromUtf8(isolate, name.data(), v8::NewStringType::kNormal).ToLocalChecked(),
|
|
357
|
+
(void)result->CreateDataProperty(context,
|
|
358
|
+
v8::String::NewFromUtf8(isolate, name.data(), v8::NewStringType::kInternalized).ToLocalChecked(),
|
|
358
359
|
d->handle());
|
|
359
360
|
}
|
|
360
361
|
else return;
|
|
@@ -365,6 +366,7 @@ void Group::GetUnlimited(v8::Local<v8::String> property, const v8::PropertyCallb
|
|
|
365
366
|
void Group::GetAttributes(v8::Local<v8::String> property, const v8::PropertyCallbackInfo<v8::Value> &info)
|
|
366
367
|
{
|
|
367
368
|
v8::Isolate *isolate = info.GetIsolate();
|
|
369
|
+
v8::Local<v8::Context> context = isolate->GetCurrentContext();
|
|
368
370
|
const auto *obj = node::ObjectWrap::Unwrap<Group>(info.Holder());
|
|
369
371
|
|
|
370
372
|
int natts = 0;
|
|
@@ -387,8 +389,8 @@ void Group::GetAttributes(v8::Local<v8::String> property, const v8::PropertyCall
|
|
|
387
389
|
return;
|
|
388
390
|
}
|
|
389
391
|
auto *a = new Attribute(name.data(), NC_GLOBAL, obj->id);
|
|
390
|
-
result->
|
|
391
|
-
v8::String::NewFromUtf8(isolate, name.data(), v8::NewStringType::
|
|
392
|
+
(void)result->CreateDataProperty(context,
|
|
393
|
+
v8::String::NewFromUtf8(isolate, name.data(), v8::NewStringType::kInternalized).ToLocalChecked(),
|
|
392
394
|
a->handle());
|
|
393
395
|
}
|
|
394
396
|
info.GetReturnValue().Set(result);
|
|
@@ -397,6 +399,7 @@ void Group::GetAttributes(v8::Local<v8::String> property, const v8::PropertyCall
|
|
|
397
399
|
void Group::GetSubgroups(v8::Local<v8::String> property, const v8::PropertyCallbackInfo<v8::Value> &info)
|
|
398
400
|
{
|
|
399
401
|
v8::Isolate *isolate = info.GetIsolate();
|
|
402
|
+
v8::Local<v8::Context> context = isolate->GetCurrentContext();
|
|
400
403
|
const auto *obj = node::ObjectWrap::Unwrap<Group>(info.Holder());
|
|
401
404
|
|
|
402
405
|
int ngrps = 0;
|
|
@@ -423,9 +426,8 @@ void Group::GetSubgroups(v8::Local<v8::String> property, const v8::PropertyCallb
|
|
|
423
426
|
auto *g = new Group(grp_ids[i]);
|
|
424
427
|
if (g->get_name(name.data()))
|
|
425
428
|
{
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
v8::String::NewFromUtf8(isolate, name.data(), v8::NewStringType::kNormal).ToLocalChecked(),
|
|
429
|
+
(void)result->CreateDataProperty(context,
|
|
430
|
+
v8::String::NewFromUtf8(isolate, name.data(), v8::NewStringType::kInternalized).ToLocalChecked(),
|
|
429
431
|
g->handle());
|
|
430
432
|
}
|
|
431
433
|
else
|
|
@@ -480,4 +482,117 @@ void Group::Inspect(const v8::FunctionCallbackInfo<v8::Value> &args)
|
|
|
480
482
|
args.GetReturnValue().Set(
|
|
481
483
|
v8::String::NewFromUtf8(isolate, "[object Group]", v8::NewStringType::kNormal).ToLocalChecked());
|
|
482
484
|
}
|
|
485
|
+
|
|
486
|
+
void Group::ToJSON(const v8::FunctionCallbackInfo<v8::Value> &args)
|
|
487
|
+
{
|
|
488
|
+
v8::Isolate *isolate = args.GetIsolate();
|
|
489
|
+
v8::Local<v8::Context> context = isolate->GetCurrentContext();
|
|
490
|
+
const auto *obj = node::ObjectWrap::Unwrap<Group>(args.Holder());
|
|
491
|
+
|
|
492
|
+
// Use internalized strings for better performance
|
|
493
|
+
v8::Local<v8::String> id_str = v8::String::NewFromUtf8Literal(isolate, "id");
|
|
494
|
+
v8::Local<v8::String> name_str = v8::String::NewFromUtf8Literal(isolate, "name");
|
|
495
|
+
v8::Local<v8::String> fullname_str = v8::String::NewFromUtf8Literal(isolate, "fullname");
|
|
496
|
+
v8::Local<v8::String> dimensions_str = v8::String::NewFromUtf8Literal(isolate, "dimensions");
|
|
497
|
+
v8::Local<v8::String> variables_str = v8::String::NewFromUtf8Literal(isolate, "variables");
|
|
498
|
+
v8::Local<v8::String> attributes_str = v8::String::NewFromUtf8Literal(isolate, "attributes");
|
|
499
|
+
v8::Local<v8::String> subgroups_str = v8::String::NewFromUtf8Literal(isolate, "subgroups");
|
|
500
|
+
|
|
501
|
+
v8::Local<v8::Object> json = v8::Object::New(isolate);
|
|
502
|
+
|
|
503
|
+
// Add id
|
|
504
|
+
(void)json->CreateDataProperty(context, id_str, v8::Integer::New(isolate, obj->id));
|
|
505
|
+
|
|
506
|
+
// Add name
|
|
507
|
+
std::array<char, NC_MAX_NAME + 1> name{};
|
|
508
|
+
if (obj->get_name(name.data()))
|
|
509
|
+
{
|
|
510
|
+
(void)json->CreateDataProperty(context, name_str,
|
|
511
|
+
v8::String::NewFromUtf8(isolate, name.data(), v8::NewStringType::kInternalized).ToLocalChecked());
|
|
512
|
+
}
|
|
513
|
+
|
|
514
|
+
// Add fullname
|
|
515
|
+
size_t len = 0;
|
|
516
|
+
int retval = nc_inq_grpname_len(obj->id, &len);
|
|
517
|
+
if (retval == NC_NOERR)
|
|
518
|
+
{
|
|
519
|
+
std::vector<char> fullname(len + 1, '\0');
|
|
520
|
+
retval = nc_inq_grpname_full(obj->id, nullptr, fullname.data());
|
|
521
|
+
if (retval == NC_NOERR)
|
|
522
|
+
{
|
|
523
|
+
(void)json->CreateDataProperty(context, fullname_str,
|
|
524
|
+
v8::String::NewFromUtf8(isolate, fullname.data(), v8::NewStringType::kInternalized).ToLocalChecked());
|
|
525
|
+
}
|
|
526
|
+
}
|
|
527
|
+
|
|
528
|
+
// Convert dimensions object to array
|
|
529
|
+
v8::Local<v8::Value> dimensions = args.Holder()->Get(context, dimensions_str).ToLocalChecked();
|
|
530
|
+
if (dimensions->IsObject() && !dimensions->IsNull())
|
|
531
|
+
{
|
|
532
|
+
v8::Local<v8::Object> dimsObj = dimensions->ToObject(context).ToLocalChecked();
|
|
533
|
+
v8::Local<v8::Array> propNames = dimsObj->GetOwnPropertyNames(context).ToLocalChecked();
|
|
534
|
+
v8::Local<v8::Array> dimsArray = v8::Array::New(isolate, propNames->Length());
|
|
535
|
+
|
|
536
|
+
for (uint32_t i = 0; i < propNames->Length(); i++)
|
|
537
|
+
{
|
|
538
|
+
v8::Local<v8::Value> key = propNames->Get(context, i).ToLocalChecked();
|
|
539
|
+
v8::Local<v8::Value> value = dimsObj->Get(context, key).ToLocalChecked();
|
|
540
|
+
(void)dimsArray->Set(context, i, value);
|
|
541
|
+
}
|
|
542
|
+
(void)json->CreateDataProperty(context, dimensions_str, dimsArray);
|
|
543
|
+
}
|
|
544
|
+
|
|
545
|
+
// Convert variables object to array
|
|
546
|
+
v8::Local<v8::Value> variables = args.Holder()->Get(context, variables_str).ToLocalChecked();
|
|
547
|
+
if (variables->IsObject() && !variables->IsNull())
|
|
548
|
+
{
|
|
549
|
+
v8::Local<v8::Object> varsObj = variables->ToObject(context).ToLocalChecked();
|
|
550
|
+
v8::Local<v8::Array> propNames = varsObj->GetOwnPropertyNames(context).ToLocalChecked();
|
|
551
|
+
v8::Local<v8::Array> varsArray = v8::Array::New(isolate, propNames->Length());
|
|
552
|
+
|
|
553
|
+
for (uint32_t i = 0; i < propNames->Length(); i++)
|
|
554
|
+
{
|
|
555
|
+
v8::Local<v8::Value> key = propNames->Get(context, i).ToLocalChecked();
|
|
556
|
+
v8::Local<v8::Value> value = varsObj->Get(context, key).ToLocalChecked();
|
|
557
|
+
(void)varsArray->Set(context, i, value);
|
|
558
|
+
}
|
|
559
|
+
(void)json->CreateDataProperty(context, variables_str, varsArray);
|
|
560
|
+
}
|
|
561
|
+
|
|
562
|
+
// Convert attributes object to array
|
|
563
|
+
v8::Local<v8::Value> attributes = args.Holder()->Get(context, attributes_str).ToLocalChecked();
|
|
564
|
+
if (attributes->IsObject() && !attributes->IsNull())
|
|
565
|
+
{
|
|
566
|
+
v8::Local<v8::Object> attrsObj = attributes->ToObject(context).ToLocalChecked();
|
|
567
|
+
v8::Local<v8::Array> propNames = attrsObj->GetOwnPropertyNames(context).ToLocalChecked();
|
|
568
|
+
v8::Local<v8::Array> attrsArray = v8::Array::New(isolate, propNames->Length());
|
|
569
|
+
|
|
570
|
+
for (uint32_t i = 0; i < propNames->Length(); i++)
|
|
571
|
+
{
|
|
572
|
+
v8::Local<v8::Value> key = propNames->Get(context, i).ToLocalChecked();
|
|
573
|
+
v8::Local<v8::Value> value = attrsObj->Get(context, key).ToLocalChecked();
|
|
574
|
+
(void)attrsArray->Set(context, i, value);
|
|
575
|
+
}
|
|
576
|
+
(void)json->CreateDataProperty(context, attributes_str, attrsArray);
|
|
577
|
+
}
|
|
578
|
+
|
|
579
|
+
// Convert subgroups object to array
|
|
580
|
+
v8::Local<v8::Value> subgroups = args.Holder()->Get(context, subgroups_str).ToLocalChecked();
|
|
581
|
+
if (subgroups->IsObject() && !subgroups->IsNull())
|
|
582
|
+
{
|
|
583
|
+
v8::Local<v8::Object> subgrpsObj = subgroups->ToObject(context).ToLocalChecked();
|
|
584
|
+
v8::Local<v8::Array> propNames = subgrpsObj->GetOwnPropertyNames(context).ToLocalChecked();
|
|
585
|
+
v8::Local<v8::Array> subgrpsArray = v8::Array::New(isolate, propNames->Length());
|
|
586
|
+
|
|
587
|
+
for (uint32_t i = 0; i < propNames->Length(); i++)
|
|
588
|
+
{
|
|
589
|
+
v8::Local<v8::Value> key = propNames->Get(context, i).ToLocalChecked();
|
|
590
|
+
v8::Local<v8::Value> value = subgrpsObj->Get(context, key).ToLocalChecked();
|
|
591
|
+
(void)subgrpsArray->Set(context, i, value);
|
|
592
|
+
}
|
|
593
|
+
(void)json->CreateDataProperty(context, subgroups_str, subgrpsArray);
|
|
594
|
+
}
|
|
595
|
+
|
|
596
|
+
args.GetReturnValue().Set(json);
|
|
597
|
+
}
|
|
483
598
|
} // namespace nodenetcdfjs
|
package/src/Group.h
CHANGED
|
@@ -40,6 +40,7 @@ class Group : public node::ObjectWrap
|
|
|
40
40
|
static void AddSubgroup(const v8::FunctionCallbackInfo<v8::Value> &args);
|
|
41
41
|
static void AddVariable(const v8::FunctionCallbackInfo<v8::Value> &args);
|
|
42
42
|
static void Inspect(const v8::FunctionCallbackInfo<v8::Value> &args);
|
|
43
|
+
static void ToJSON(const v8::FunctionCallbackInfo<v8::Value> &args);
|
|
43
44
|
|
|
44
45
|
int id{-1};
|
|
45
46
|
};
|
package/src/Variable.cpp
CHANGED
|
@@ -42,6 +42,7 @@ void Variable::Init(v8::Local<v8::Object> exports)
|
|
|
42
42
|
NODE_SET_PROTOTYPE_METHOD(tpl, "writeStridedSlice", Variable::WriteStridedSlice);
|
|
43
43
|
NODE_SET_PROTOTYPE_METHOD(tpl, "addAttribute", Variable::AddAttribute);
|
|
44
44
|
NODE_SET_PROTOTYPE_METHOD(tpl, "inspect", Variable::Inspect);
|
|
45
|
+
NODE_SET_PROTOTYPE_METHOD(tpl, "toJSON", Variable::ToJSON);
|
|
45
46
|
tpl->InstanceTemplate()->SetAccessor(
|
|
46
47
|
v8::String::NewFromUtf8(isolate, "id", v8::NewStringType::kNormal).ToLocalChecked(), Variable::GetId);
|
|
47
48
|
tpl->InstanceTemplate()->SetAccessor(
|
|
@@ -678,6 +679,7 @@ void Variable::GetDimensions(v8::Local<v8::String> property, const v8::PropertyC
|
|
|
678
679
|
void Variable::GetAttributes(v8::Local<v8::String> property, const v8::PropertyCallbackInfo<v8::Value> &info)
|
|
679
680
|
{
|
|
680
681
|
v8::Isolate *isolate = info.GetIsolate();
|
|
682
|
+
v8::Local<v8::Context> context = isolate->GetCurrentContext();
|
|
681
683
|
Variable *obj = node::ObjectWrap::Unwrap<Variable>(info.Holder());
|
|
682
684
|
int natts;
|
|
683
685
|
int retval = nc_inq_varnatts(obj->parent_id, obj->id, &natts);
|
|
@@ -697,8 +699,8 @@ void Variable::GetAttributes(v8::Local<v8::String> property, const v8::PropertyC
|
|
|
697
699
|
return;
|
|
698
700
|
}
|
|
699
701
|
Attribute *a = new Attribute(name, obj->id, obj->parent_id);
|
|
700
|
-
result->
|
|
701
|
-
v8::String::NewFromUtf8(isolate, name, v8::NewStringType::
|
|
702
|
+
(void)result->CreateDataProperty(context,
|
|
703
|
+
v8::String::NewFromUtf8(isolate, name, v8::NewStringType::kInternalized).ToLocalChecked(), a->handle());
|
|
702
704
|
}
|
|
703
705
|
info.GetReturnValue().Set(result);
|
|
704
706
|
}
|
|
@@ -1349,4 +1351,62 @@ void Variable::Inspect(const v8::FunctionCallbackInfo<v8::Value> &args)
|
|
|
1349
1351
|
args.GetReturnValue().Set(
|
|
1350
1352
|
v8::String::NewFromUtf8(isolate, "[object Variable]", v8::NewStringType::kNormal).ToLocalChecked());
|
|
1351
1353
|
}
|
|
1354
|
+
|
|
1355
|
+
void Variable::ToJSON(const v8::FunctionCallbackInfo<v8::Value> &args)
|
|
1356
|
+
{
|
|
1357
|
+
v8::Isolate *isolate = args.GetIsolate();
|
|
1358
|
+
v8::Local<v8::Context> context = isolate->GetCurrentContext();
|
|
1359
|
+
const auto *obj = node::ObjectWrap::Unwrap<Variable>(args.Holder());
|
|
1360
|
+
|
|
1361
|
+
// Use internalized strings for better performance
|
|
1362
|
+
v8::Local<v8::String> id_str = v8::String::NewFromUtf8Literal(isolate, "id");
|
|
1363
|
+
v8::Local<v8::String> name_str = v8::String::NewFromUtf8Literal(isolate, "name");
|
|
1364
|
+
v8::Local<v8::String> type_str = v8::String::NewFromUtf8Literal(isolate, "type");
|
|
1365
|
+
v8::Local<v8::String> dimensions_str = v8::String::NewFromUtf8Literal(isolate, "dimensions");
|
|
1366
|
+
v8::Local<v8::String> attributes_str = v8::String::NewFromUtf8Literal(isolate, "attributes");
|
|
1367
|
+
|
|
1368
|
+
v8::Local<v8::Object> json = v8::Object::New(isolate);
|
|
1369
|
+
|
|
1370
|
+
// Add id
|
|
1371
|
+
(void)json->CreateDataProperty(context, id_str, v8::Integer::New(isolate, obj->id));
|
|
1372
|
+
|
|
1373
|
+
// Add name
|
|
1374
|
+
char name[NC_MAX_NAME + 1];
|
|
1375
|
+
if (obj->get_name(name))
|
|
1376
|
+
{
|
|
1377
|
+
(void)json->CreateDataProperty(context, name_str,
|
|
1378
|
+
v8::String::NewFromUtf8(isolate, name, v8::NewStringType::kInternalized).ToLocalChecked());
|
|
1379
|
+
}
|
|
1380
|
+
|
|
1381
|
+
// Add type
|
|
1382
|
+
const char *type_name = (obj->type < NC_BYTE || obj->type > NC_UINT) ? "unknown" : type_names[obj->type];
|
|
1383
|
+
(void)json->CreateDataProperty(context, type_str,
|
|
1384
|
+
v8::String::NewFromUtf8(isolate, type_name, v8::NewStringType::kInternalized).ToLocalChecked());
|
|
1385
|
+
|
|
1386
|
+
// Dimensions are already an array, just add them
|
|
1387
|
+
v8::Local<v8::Value> dimensions = args.Holder()->Get(context, dimensions_str).ToLocalChecked();
|
|
1388
|
+
if (dimensions->IsArray())
|
|
1389
|
+
{
|
|
1390
|
+
(void)json->CreateDataProperty(context, dimensions_str, dimensions);
|
|
1391
|
+
}
|
|
1392
|
+
|
|
1393
|
+
// Convert attributes object to array
|
|
1394
|
+
v8::Local<v8::Value> attributes = args.Holder()->Get(context, attributes_str).ToLocalChecked();
|
|
1395
|
+
if (attributes->IsObject() && !attributes->IsNull())
|
|
1396
|
+
{
|
|
1397
|
+
v8::Local<v8::Object> attrsObj = attributes->ToObject(context).ToLocalChecked();
|
|
1398
|
+
v8::Local<v8::Array> propNames = attrsObj->GetOwnPropertyNames(context).ToLocalChecked();
|
|
1399
|
+
v8::Local<v8::Array> attrsArray = v8::Array::New(isolate, propNames->Length());
|
|
1400
|
+
|
|
1401
|
+
for (uint32_t i = 0; i < propNames->Length(); i++)
|
|
1402
|
+
{
|
|
1403
|
+
v8::Local<v8::Value> key = propNames->Get(context, i).ToLocalChecked();
|
|
1404
|
+
v8::Local<v8::Value> value = attrsObj->Get(context, key).ToLocalChecked();
|
|
1405
|
+
(void)attrsArray->Set(context, i, value);
|
|
1406
|
+
}
|
|
1407
|
+
(void)json->CreateDataProperty(context, attributes_str, attrsArray);
|
|
1408
|
+
}
|
|
1409
|
+
|
|
1410
|
+
args.GetReturnValue().Set(json);
|
|
1411
|
+
}
|
|
1352
1412
|
} // namespace nodenetcdfjs
|
package/src/Variable.h
CHANGED
|
@@ -68,6 +68,7 @@ class Variable : public node::ObjectWrap
|
|
|
68
68
|
static void SetCompressionLevel(v8::Local<v8::String> property, v8::Local<v8::Value> val,
|
|
69
69
|
const v8::PropertyCallbackInfo<void> &info);
|
|
70
70
|
static void Inspect(const v8::FunctionCallbackInfo<v8::Value> &args);
|
|
71
|
+
static void ToJSON(const v8::FunctionCallbackInfo<v8::Value> &args);
|
|
71
72
|
|
|
72
73
|
static v8::Persistent<v8::Function> constructor;
|
|
73
74
|
|
package/test/attribute.js
CHANGED
|
@@ -33,4 +33,66 @@ describe('Attribute', function() {
|
|
|
33
33
|
expect(attributes["name"].value).to.equal("air_pressure");
|
|
34
34
|
});
|
|
35
35
|
});
|
|
36
|
+
|
|
37
|
+
describe('toJSON', function() {
|
|
38
|
+
it('should serialize attribute without crashing', function() {
|
|
39
|
+
var file = new nodenetcdf.File("test/test_hgroups.nc", "r");
|
|
40
|
+
var attributes = file.root.subgroups["mozaic_flight_2012030419144751_ascent"].attributes;
|
|
41
|
+
var attr = attributes["airport_dep"];
|
|
42
|
+
|
|
43
|
+
// Explicitly call toJSON to test the V8 ToLocalChecked fix
|
|
44
|
+
var json = attr.toJSON();
|
|
45
|
+
|
|
46
|
+
expect(json).to.be.an('object');
|
|
47
|
+
expect(json).to.have.property('name', 'airport_dep');
|
|
48
|
+
expect(json).to.have.property('value', 'FRA');
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
it('should handle variable attribute toJSON without crashing', function() {
|
|
52
|
+
var file = new nodenetcdf.File("test/test_hgroups.nc", "r");
|
|
53
|
+
var attributes = file.root.subgroups["mozaic_flight_2012030419144751_ascent"].variables["air_press"].attributes;
|
|
54
|
+
var attr = attributes["name"];
|
|
55
|
+
|
|
56
|
+
// Explicitly call toJSON to test the V8 ToLocalChecked fix
|
|
57
|
+
var json = attr.toJSON();
|
|
58
|
+
|
|
59
|
+
expect(json).to.be.an('object');
|
|
60
|
+
expect(json).to.have.property('name', 'name');
|
|
61
|
+
expect(json).to.have.property('value', 'air_pressure');
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
it('should work with JSON.stringify', function() {
|
|
65
|
+
var file = new nodenetcdf.File("test/test_hgroups.nc", "r");
|
|
66
|
+
var attributes = file.root.subgroups["mozaic_flight_2012030419144751_ascent"].attributes;
|
|
67
|
+
var attr = attributes["airport_dep"];
|
|
68
|
+
|
|
69
|
+
// Test that JSON.stringify (which calls toJSON internally) works
|
|
70
|
+
var jsonString = JSON.stringify(attr);
|
|
71
|
+
var json = JSON.parse(jsonString);
|
|
72
|
+
|
|
73
|
+
expect(json).to.be.an('object');
|
|
74
|
+
expect(json).to.have.property('name', 'airport_dep');
|
|
75
|
+
expect(json).to.have.property('value', 'FRA');
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
it('should handle errors gracefully and return null value on type conversion errors', function() {
|
|
79
|
+
var file = new nodenetcdf.File("test/test_hgroups.nc", "r");
|
|
80
|
+
var attributes = file.root.subgroups["mozaic_flight_2012030419144751_ascent"].attributes;
|
|
81
|
+
|
|
82
|
+
// Iterate through all attributes to find any that might have type issues
|
|
83
|
+
// The toJSON method should handle these gracefully
|
|
84
|
+
for (var attrName in attributes) {
|
|
85
|
+
var attr = attributes[attrName];
|
|
86
|
+
|
|
87
|
+
// This should not crash even if there are type conversion errors
|
|
88
|
+
var json = attr.toJSON();
|
|
89
|
+
|
|
90
|
+
expect(json).to.be.an('object');
|
|
91
|
+
expect(json).to.have.property('name');
|
|
92
|
+
expect(json).to.have.property('value');
|
|
93
|
+
// Value should either be a valid value or null if there was an error
|
|
94
|
+
expect(json.value === null || typeof json.value !== 'undefined').to.be.true;
|
|
95
|
+
}
|
|
96
|
+
});
|
|
97
|
+
});
|
|
36
98
|
});
|
package/test/json.js
ADDED
|
@@ -0,0 +1,174 @@
|
|
|
1
|
+
var expect = require("chai").expect,
|
|
2
|
+
nodenetcdf = require("../build/Release/nodenetcdf.node");
|
|
3
|
+
|
|
4
|
+
describe('JSON Serialization', function() {
|
|
5
|
+
var file;
|
|
6
|
+
|
|
7
|
+
beforeEach(function() {
|
|
8
|
+
file = new nodenetcdf.File("test/testrh.nc", "r");
|
|
9
|
+
});
|
|
10
|
+
|
|
11
|
+
afterEach(function() {
|
|
12
|
+
file.close();
|
|
13
|
+
});
|
|
14
|
+
|
|
15
|
+
describe('Dimension', function() {
|
|
16
|
+
it('should serialize dimension to JSON', function() {
|
|
17
|
+
var dim = file.root.dimensions['dim1'];
|
|
18
|
+
var json = JSON.parse(JSON.stringify(dim));
|
|
19
|
+
|
|
20
|
+
expect(json).to.be.an('object');
|
|
21
|
+
expect(json).to.have.property('id');
|
|
22
|
+
expect(json).to.have.property('name', 'dim1');
|
|
23
|
+
expect(json).to.have.property('length', 10000);
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
it('should have all required properties in JSON', function() {
|
|
27
|
+
var dim = file.root.dimensions['dim1'];
|
|
28
|
+
var json = JSON.parse(JSON.stringify(dim));
|
|
29
|
+
|
|
30
|
+
expect(json.id).to.be.a('number');
|
|
31
|
+
expect(json.name).to.be.a('string');
|
|
32
|
+
expect(json.length).to.be.a('number');
|
|
33
|
+
});
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
describe('Attribute', function() {
|
|
37
|
+
it('should serialize group attribute to JSON', function() {
|
|
38
|
+
var attributes = file.root.attributes;
|
|
39
|
+
for (var attrName in attributes) {
|
|
40
|
+
var attr = attributes[attrName];
|
|
41
|
+
var json = JSON.parse(JSON.stringify(attr));
|
|
42
|
+
|
|
43
|
+
expect(json).to.be.an('object');
|
|
44
|
+
expect(json).to.have.property('name');
|
|
45
|
+
expect(json).to.have.property('value');
|
|
46
|
+
return; // Test first attribute
|
|
47
|
+
}
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
it('should serialize variable attribute to JSON', function() {
|
|
51
|
+
var variables = file.root.variables;
|
|
52
|
+
for (var varName in variables) {
|
|
53
|
+
var variable = variables[varName];
|
|
54
|
+
var varAttrs = variable.attributes;
|
|
55
|
+
for (var attrName in varAttrs) {
|
|
56
|
+
var attr = varAttrs[attrName];
|
|
57
|
+
var json = JSON.parse(JSON.stringify(attr));
|
|
58
|
+
|
|
59
|
+
expect(json).to.be.an('object');
|
|
60
|
+
expect(json).to.have.property('name');
|
|
61
|
+
expect(json).to.have.property('value');
|
|
62
|
+
return; // Test first attribute
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
it('should not serialize attribute values as null', function() {
|
|
68
|
+
// Check all attributes in file don't have null values when serialized
|
|
69
|
+
var jsonStr = JSON.stringify(file);
|
|
70
|
+
var nullMatches = jsonStr.match(/"value":\s*null/g);
|
|
71
|
+
var nullCount = nullMatches ? nullMatches.length : 0;
|
|
72
|
+
|
|
73
|
+
expect(nullCount).to.equal(0, 'Found ' + nullCount + ' null attribute values in JSON');
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
it('should convert TypedArray values to regular arrays in JSON', function() {
|
|
77
|
+
// Find an attribute with a TypedArray value
|
|
78
|
+
var variables = file.root.variables;
|
|
79
|
+
for (var varName in variables) {
|
|
80
|
+
var variable = variables[varName];
|
|
81
|
+
var varAttrs = variable.attributes;
|
|
82
|
+
for (var attrName in varAttrs) {
|
|
83
|
+
var attr = varAttrs[attrName];
|
|
84
|
+
|
|
85
|
+
// Check if value is a TypedArray
|
|
86
|
+
if (attr.value && typeof attr.value === 'object' &&
|
|
87
|
+
attr.value.constructor && attr.value.constructor.name.includes('Array') &&
|
|
88
|
+
!Array.isArray(attr.value)) {
|
|
89
|
+
|
|
90
|
+
// It's a TypedArray, test it converts to regular array
|
|
91
|
+
var json = JSON.parse(JSON.stringify(attr));
|
|
92
|
+
|
|
93
|
+
// In JSON, it should be a regular array
|
|
94
|
+
expect(Array.isArray(json.value)).to.be.true;
|
|
95
|
+
return;
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
});
|
|
100
|
+
});
|
|
101
|
+
|
|
102
|
+
describe('Variable', function() {
|
|
103
|
+
it('should serialize variable to JSON', function() {
|
|
104
|
+
var variable = file.root.variables['var1'];
|
|
105
|
+
var json = JSON.parse(JSON.stringify(variable));
|
|
106
|
+
|
|
107
|
+
expect(json).to.be.an('object');
|
|
108
|
+
expect(json).to.have.property('id');
|
|
109
|
+
expect(json).to.have.property('name', 'var1');
|
|
110
|
+
expect(json).to.have.property('type', 'double');
|
|
111
|
+
});
|
|
112
|
+
|
|
113
|
+
it('should have all required properties in JSON', function() {
|
|
114
|
+
var variable = file.root.variables['var1'];
|
|
115
|
+
var json = JSON.parse(JSON.stringify(variable));
|
|
116
|
+
|
|
117
|
+
expect(json.id).to.be.a('number');
|
|
118
|
+
expect(json.name).to.be.a('string');
|
|
119
|
+
expect(json.type).to.be.a('string');
|
|
120
|
+
});
|
|
121
|
+
});
|
|
122
|
+
|
|
123
|
+
describe('Group', function() {
|
|
124
|
+
it('should serialize group to JSON', function() {
|
|
125
|
+
var group = file.root;
|
|
126
|
+
var json = JSON.parse(JSON.stringify(group));
|
|
127
|
+
|
|
128
|
+
expect(json).to.be.an('object');
|
|
129
|
+
expect(json).to.have.property('id');
|
|
130
|
+
expect(json).to.have.property('name');
|
|
131
|
+
expect(json).to.have.property('fullname');
|
|
132
|
+
});
|
|
133
|
+
|
|
134
|
+
it('should have all required properties in JSON', function() {
|
|
135
|
+
var group = file.root;
|
|
136
|
+
var json = JSON.parse(JSON.stringify(group));
|
|
137
|
+
|
|
138
|
+
expect(json.id).to.be.a('number');
|
|
139
|
+
expect(json.name).to.be.a('string');
|
|
140
|
+
expect(json.fullname).to.be.a('string');
|
|
141
|
+
});
|
|
142
|
+
|
|
143
|
+
it('should recursively serialize all children', function() {
|
|
144
|
+
var group = file.root;
|
|
145
|
+
var json = JSON.parse(JSON.stringify(group));
|
|
146
|
+
|
|
147
|
+
expect(json).to.have.property('dimensions');
|
|
148
|
+
expect(json).to.have.property('variables');
|
|
149
|
+
expect(json).to.have.property('attributes');
|
|
150
|
+
expect(json).to.have.property('subgroups');
|
|
151
|
+
});
|
|
152
|
+
});
|
|
153
|
+
|
|
154
|
+
describe('File', function() {
|
|
155
|
+
it('should serialize file to JSON', function() {
|
|
156
|
+
var json = JSON.parse(JSON.stringify(file));
|
|
157
|
+
|
|
158
|
+
expect(json).to.be.an('object');
|
|
159
|
+
expect(json).to.have.property('id');
|
|
160
|
+
expect(json).to.have.property('name');
|
|
161
|
+
expect(json).to.have.property('fullname');
|
|
162
|
+
});
|
|
163
|
+
|
|
164
|
+
it('should recursively serialize entire file structure', function() {
|
|
165
|
+
var json = JSON.parse(JSON.stringify(file));
|
|
166
|
+
|
|
167
|
+
// File should serialize as root group with all children
|
|
168
|
+
expect(json).to.have.property('dimensions');
|
|
169
|
+
expect(json).to.have.property('variables');
|
|
170
|
+
expect(json.variables).to.be.an('object');
|
|
171
|
+
expect(json.dimensions).to.be.an('object');
|
|
172
|
+
});
|
|
173
|
+
});
|
|
174
|
+
});
|