nodenetcdf 4.9.31 → 4.9.33

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "nodenetcdf",
3
- "version": "4.9.31",
3
+ "version": "4.9.33",
4
4
  "description": "Read and write NodeNetCDF files",
5
5
  "main": "./build/Release/nodenetcdf.node",
6
6
  "types": "./index.d.ts",
package/src/Attribute.cpp CHANGED
@@ -365,6 +365,21 @@ void Attribute::ToJSON(const v8::FunctionCallbackInfo<v8::Value> &args)
365
365
  }
366
366
  value = v8::Null(isolate);
367
367
  }
368
+ else if (value->IsTypedArray())
369
+ {
370
+ // Convert TypedArray to regular array for JSON serialization
371
+ v8::Local<v8::TypedArray> typedArray = v8::Local<v8::TypedArray>::Cast(value);
372
+ uint32_t length = typedArray->Length();
373
+ v8::Local<v8::Array> array = v8::Array::New(isolate, length);
374
+
375
+ for (uint32_t i = 0; i < length; i++)
376
+ {
377
+ v8::Local<v8::Value> element = typedArray->Get(context, i).ToLocalChecked();
378
+ (void)array->Set(context, i, element);
379
+ }
380
+ value = array;
381
+ }
382
+ // Don't wrap primitives - only convert TypedArrays to arrays
368
383
 
369
384
  (void)json->CreateDataProperty(context, value_str, value);
370
385
 
package/src/File.cpp CHANGED
@@ -174,11 +174,27 @@ void File::ToJSON(const v8::FunctionCallbackInfo<v8::Value> &args)
174
174
  v8::Isolate *isolate = args.GetIsolate();
175
175
  v8::Local<v8::Context> context = isolate->GetCurrentContext();
176
176
 
177
- // Get the root group and serialize it
177
+ // Get the root group
178
178
  v8::Local<v8::String> rootProp = v8::String::NewFromUtf8(isolate, "root", v8::NewStringType::kNormal).ToLocalChecked();
179
179
  v8::Local<v8::Value> root = args.Holder()->Get(context, rootProp).ToLocalChecked();
180
180
 
181
- // The root group's toJSON will automatically serialize all children recursively
181
+ // Call toJSON on the root group to get proper array conversion
182
+ if (root->IsObject())
183
+ {
184
+ v8::Local<v8::Object> rootObj = root->ToObject(context).ToLocalChecked();
185
+ v8::Local<v8::String> toJSONProp = v8::String::NewFromUtf8(isolate, "toJSON", v8::NewStringType::kNormal).ToLocalChecked();
186
+ v8::Local<v8::Value> toJSONMethod = rootObj->Get(context, toJSONProp).ToLocalChecked();
187
+
188
+ if (toJSONMethod->IsFunction())
189
+ {
190
+ v8::Local<v8::Function> toJSONFunc = v8::Local<v8::Function>::Cast(toJSONMethod);
191
+ v8::Local<v8::Value> result = toJSONFunc->Call(context, rootObj, 0, nullptr).ToLocalChecked();
192
+ args.GetReturnValue().Set(result);
193
+ return;
194
+ }
195
+ }
196
+
197
+ // Fallback: return root as-is
182
198
  args.GetReturnValue().Set(root);
183
199
  }
184
200
  } // namespace nodenetcdfjs
package/src/Group.cpp CHANGED
@@ -525,30 +525,124 @@ void Group::ToJSON(const v8::FunctionCallbackInfo<v8::Value> &args)
525
525
  }
526
526
  }
527
527
 
528
- // Add dimensions, variables, attributes, subgroups
529
- // These will be accessed through property getters which trigger the respective Get* methods
528
+ // Convert dimensions object to array, calling toJSON on each item
530
529
  v8::Local<v8::Value> dimensions = args.Holder()->Get(context, dimensions_str).ToLocalChecked();
531
- if (dimensions->IsObject())
532
- {
533
- (void)json->CreateDataProperty(context, dimensions_str, dimensions);
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
+ v8::Local<v8::String> toJSONStr = v8::String::NewFromUtf8Literal(isolate, "toJSON");
536
+
537
+ for (uint32_t i = 0; i < propNames->Length(); i++)
538
+ {
539
+ v8::Local<v8::Value> key = propNames->Get(context, i).ToLocalChecked();
540
+ v8::Local<v8::Value> value = dimsObj->Get(context, key).ToLocalChecked();
541
+
542
+ // Call toJSON if available to ensure proper serialization
543
+ if (value->IsObject())
544
+ {
545
+ v8::Local<v8::Object> valueObj = value->ToObject(context).ToLocalChecked();
546
+ v8::Local<v8::Value> toJSON = valueObj->Get(context, toJSONStr).ToLocalChecked();
547
+ if (toJSON->IsFunction())
548
+ {
549
+ v8::Local<v8::Function> toJSONFunc = v8::Local<v8::Function>::Cast(toJSON);
550
+ value = toJSONFunc->Call(context, valueObj, 0, nullptr).ToLocalChecked();
551
+ }
552
+ }
553
+ (void)dimsArray->Set(context, i, value);
554
+ }
555
+ (void)json->CreateDataProperty(context, dimensions_str, dimsArray);
534
556
  }
535
557
 
558
+ // Convert variables object to array, calling toJSON on each item
536
559
  v8::Local<v8::Value> variables = args.Holder()->Get(context, variables_str).ToLocalChecked();
537
- if (variables->IsObject())
538
- {
539
- (void)json->CreateDataProperty(context, variables_str, variables);
560
+ if (variables->IsObject() && !variables->IsNull())
561
+ {
562
+ v8::Local<v8::Object> varsObj = variables->ToObject(context).ToLocalChecked();
563
+ v8::Local<v8::Array> propNames = varsObj->GetOwnPropertyNames(context).ToLocalChecked();
564
+ v8::Local<v8::Array> varsArray = v8::Array::New(isolate, propNames->Length());
565
+ v8::Local<v8::String> toJSONStr = v8::String::NewFromUtf8Literal(isolate, "toJSON");
566
+
567
+ for (uint32_t i = 0; i < propNames->Length(); i++)
568
+ {
569
+ v8::Local<v8::Value> key = propNames->Get(context, i).ToLocalChecked();
570
+ v8::Local<v8::Value> value = varsObj->Get(context, key).ToLocalChecked();
571
+
572
+ // Call toJSON if available to ensure proper serialization
573
+ if (value->IsObject())
574
+ {
575
+ v8::Local<v8::Object> valueObj = value->ToObject(context).ToLocalChecked();
576
+ v8::Local<v8::Value> toJSON = valueObj->Get(context, toJSONStr).ToLocalChecked();
577
+ if (toJSON->IsFunction())
578
+ {
579
+ v8::Local<v8::Function> toJSONFunc = v8::Local<v8::Function>::Cast(toJSON);
580
+ value = toJSONFunc->Call(context, valueObj, 0, nullptr).ToLocalChecked();
581
+ }
582
+ }
583
+ (void)varsArray->Set(context, i, value);
584
+ }
585
+ (void)json->CreateDataProperty(context, variables_str, varsArray);
540
586
  }
541
587
 
588
+ // Convert attributes object to array, calling toJSON on each item
542
589
  v8::Local<v8::Value> attributes = args.Holder()->Get(context, attributes_str).ToLocalChecked();
543
- if (attributes->IsObject())
544
- {
545
- (void)json->CreateDataProperty(context, attributes_str, attributes);
590
+ if (attributes->IsObject() && !attributes->IsNull())
591
+ {
592
+ v8::Local<v8::Object> attrsObj = attributes->ToObject(context).ToLocalChecked();
593
+ v8::Local<v8::Array> propNames = attrsObj->GetOwnPropertyNames(context).ToLocalChecked();
594
+ v8::Local<v8::Array> attrsArray = v8::Array::New(isolate, propNames->Length());
595
+ v8::Local<v8::String> toJSONStr = v8::String::NewFromUtf8Literal(isolate, "toJSON");
596
+
597
+ for (uint32_t i = 0; i < propNames->Length(); i++)
598
+ {
599
+ v8::Local<v8::Value> key = propNames->Get(context, i).ToLocalChecked();
600
+ v8::Local<v8::Value> value = attrsObj->Get(context, key).ToLocalChecked();
601
+
602
+ // Call toJSON if available to ensure proper serialization
603
+ if (value->IsObject())
604
+ {
605
+ v8::Local<v8::Object> valueObj = value->ToObject(context).ToLocalChecked();
606
+ v8::Local<v8::Value> toJSON = valueObj->Get(context, toJSONStr).ToLocalChecked();
607
+ if (toJSON->IsFunction())
608
+ {
609
+ v8::Local<v8::Function> toJSONFunc = v8::Local<v8::Function>::Cast(toJSON);
610
+ value = toJSONFunc->Call(context, valueObj, 0, nullptr).ToLocalChecked();
611
+ }
612
+ }
613
+ (void)attrsArray->Set(context, i, value);
614
+ }
615
+ (void)json->CreateDataProperty(context, attributes_str, attrsArray);
546
616
  }
547
617
 
618
+ // Convert subgroups object to array, calling toJSON on each item
548
619
  v8::Local<v8::Value> subgroups = args.Holder()->Get(context, subgroups_str).ToLocalChecked();
549
- if (subgroups->IsObject())
550
- {
551
- (void)json->CreateDataProperty(context, subgroups_str, subgroups);
620
+ if (subgroups->IsObject() && !subgroups->IsNull())
621
+ {
622
+ v8::Local<v8::Object> subgrpsObj = subgroups->ToObject(context).ToLocalChecked();
623
+ v8::Local<v8::Array> propNames = subgrpsObj->GetOwnPropertyNames(context).ToLocalChecked();
624
+ v8::Local<v8::Array> subgrpsArray = v8::Array::New(isolate, propNames->Length());
625
+ v8::Local<v8::String> toJSONStr = v8::String::NewFromUtf8Literal(isolate, "toJSON");
626
+
627
+ for (uint32_t i = 0; i < propNames->Length(); i++)
628
+ {
629
+ v8::Local<v8::Value> key = propNames->Get(context, i).ToLocalChecked();
630
+ v8::Local<v8::Value> value = subgrpsObj->Get(context, key).ToLocalChecked();
631
+
632
+ // Call toJSON if available to ensure proper serialization
633
+ if (value->IsObject())
634
+ {
635
+ v8::Local<v8::Object> valueObj = value->ToObject(context).ToLocalChecked();
636
+ v8::Local<v8::Value> toJSON = valueObj->Get(context, toJSONStr).ToLocalChecked();
637
+ if (toJSON->IsFunction())
638
+ {
639
+ v8::Local<v8::Function> toJSONFunc = v8::Local<v8::Function>::Cast(toJSON);
640
+ value = toJSONFunc->Call(context, valueObj, 0, nullptr).ToLocalChecked();
641
+ }
642
+ }
643
+ (void)subgrpsArray->Set(context, i, value);
644
+ }
645
+ (void)json->CreateDataProperty(context, subgroups_str, subgrpsArray);
552
646
  }
553
647
 
554
648
  args.GetReturnValue().Set(json);
package/src/Variable.cpp CHANGED
@@ -1383,18 +1383,62 @@ void Variable::ToJSON(const v8::FunctionCallbackInfo<v8::Value> &args)
1383
1383
  (void)json->CreateDataProperty(context, type_str,
1384
1384
  v8::String::NewFromUtf8(isolate, type_name, v8::NewStringType::kInternalized).ToLocalChecked());
1385
1385
 
1386
- // Add dimensions array with serialized dimension objects
1386
+ // Dimensions are already an array, but call toJSON on each item
1387
1387
  v8::Local<v8::Value> dimensions = args.Holder()->Get(context, dimensions_str).ToLocalChecked();
1388
1388
  if (dimensions->IsArray())
1389
1389
  {
1390
- (void)json->CreateDataProperty(context, dimensions_str, dimensions);
1390
+ v8::Local<v8::Array> dimsArray = v8::Local<v8::Array>::Cast(dimensions);
1391
+ v8::Local<v8::Array> newDimsArray = v8::Array::New(isolate, dimsArray->Length());
1392
+ v8::Local<v8::String> toJSONStr = v8::String::NewFromUtf8Literal(isolate, "toJSON");
1393
+
1394
+ for (uint32_t i = 0; i < dimsArray->Length(); i++)
1395
+ {
1396
+ v8::Local<v8::Value> value = dimsArray->Get(context, i).ToLocalChecked();
1397
+
1398
+ // Call toJSON if available to ensure proper serialization
1399
+ if (value->IsObject())
1400
+ {
1401
+ v8::Local<v8::Object> valueObj = value->ToObject(context).ToLocalChecked();
1402
+ v8::Local<v8::Value> toJSON = valueObj->Get(context, toJSONStr).ToLocalChecked();
1403
+ if (toJSON->IsFunction())
1404
+ {
1405
+ v8::Local<v8::Function> toJSONFunc = v8::Local<v8::Function>::Cast(toJSON);
1406
+ value = toJSONFunc->Call(context, valueObj, 0, nullptr).ToLocalChecked();
1407
+ }
1408
+ }
1409
+ (void)newDimsArray->Set(context, i, value);
1410
+ }
1411
+ (void)json->CreateDataProperty(context, dimensions_str, newDimsArray);
1391
1412
  }
1392
1413
 
1393
- // Add attributes object with serialized attribute objects
1414
+ // Convert attributes object to array, calling toJSON on each item
1394
1415
  v8::Local<v8::Value> attributes = args.Holder()->Get(context, attributes_str).ToLocalChecked();
1395
- if (attributes->IsObject())
1396
- {
1397
- (void)json->CreateDataProperty(context, attributes_str, attributes);
1416
+ if (attributes->IsObject() && !attributes->IsNull())
1417
+ {
1418
+ v8::Local<v8::Object> attrsObj = attributes->ToObject(context).ToLocalChecked();
1419
+ v8::Local<v8::Array> propNames = attrsObj->GetOwnPropertyNames(context).ToLocalChecked();
1420
+ v8::Local<v8::Array> attrsArray = v8::Array::New(isolate, propNames->Length());
1421
+ v8::Local<v8::String> toJSONStr = v8::String::NewFromUtf8Literal(isolate, "toJSON");
1422
+
1423
+ for (uint32_t i = 0; i < propNames->Length(); i++)
1424
+ {
1425
+ v8::Local<v8::Value> key = propNames->Get(context, i).ToLocalChecked();
1426
+ v8::Local<v8::Value> value = attrsObj->Get(context, key).ToLocalChecked();
1427
+
1428
+ // Call toJSON if available to ensure proper serialization
1429
+ if (value->IsObject())
1430
+ {
1431
+ v8::Local<v8::Object> valueObj = value->ToObject(context).ToLocalChecked();
1432
+ v8::Local<v8::Value> toJSON = valueObj->Get(context, toJSONStr).ToLocalChecked();
1433
+ if (toJSON->IsFunction())
1434
+ {
1435
+ v8::Local<v8::Function> toJSONFunc = v8::Local<v8::Function>::Cast(toJSON);
1436
+ value = toJSONFunc->Call(context, valueObj, 0, nullptr).ToLocalChecked();
1437
+ }
1438
+ }
1439
+ (void)attrsArray->Set(context, i, value);
1440
+ }
1441
+ (void)json->CreateDataProperty(context, attributes_str, attrsArray);
1398
1442
  }
1399
1443
 
1400
1444
  args.GetReturnValue().Set(json);
package/test/json.js CHANGED
@@ -63,6 +63,40 @@ describe('JSON Serialization', function() {
63
63
  }
64
64
  }
65
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
+ });
66
100
  });
67
101
 
68
102
  describe('Variable', function() {
@@ -130,11 +164,15 @@ describe('JSON Serialization', function() {
130
164
  it('should recursively serialize entire file structure', function() {
131
165
  var json = JSON.parse(JSON.stringify(file));
132
166
 
133
- // File should serialize as root group with all children
167
+ // File should serialize as root group with all children as arrays
134
168
  expect(json).to.have.property('dimensions');
135
169
  expect(json).to.have.property('variables');
136
- expect(json.variables).to.be.an('object');
137
- expect(json.dimensions).to.be.an('object');
170
+ expect(json).to.have.property('attributes');
171
+ expect(json).to.have.property('subgroups');
172
+ expect(json.variables).to.be.an('array');
173
+ expect(json.dimensions).to.be.an('array');
174
+ expect(json.attributes).to.be.an('array');
175
+ expect(json.subgroups).to.be.an('array');
138
176
  });
139
177
  });
140
178
  });