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/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->Set(isolate->GetCurrentContext(),
274
- v8::String::NewFromUtf8(isolate, name.data(), v8::NewStringType::kNormal).ToLocalChecked(),
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
- // Suppress warning about unused return value
315
- (void)result->Set(
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
- // Suppress warning about unused return value
356
- (void)result->Set(isolate->GetCurrentContext(),
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->Set(isolate->GetCurrentContext(),
391
- v8::String::NewFromUtf8(isolate, name.data(), v8::NewStringType::kNormal).ToLocalChecked(),
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
- // Suppress warning about unused return value
427
- (void)result->Set(isolate->GetCurrentContext(),
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->Set(isolate->GetCurrentContext(),
701
- v8::String::NewFromUtf8(isolate, name, v8::NewStringType::kNormal).ToLocalChecked(), a->handle());
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
+ });