ecere/sys/JSON: Fixed support for writing struct properties
[sdk] / ecere / src / sys / JSON.ec
1 namespace sys;
2
3 import "System"
4 import "Array"
5
6 default:
7 __attribute__((unused)) static void UnusedFunction()
8 {
9    int a;
10    a.OnGetDataFromString(null);
11    a.OnGetString(null, 0, 0);
12    a.OnFree();
13 }
14 extern int __ecereVMethodID_class_OnGetDataFromString;
15 extern int __ecereVMethodID_class_OnGetString;
16 extern int __ecereVMethodID_class_OnFree;
17 private:
18
19 public enum JSONResult { syntaxError, success, typeMismatch, noItem };
20
21 public enum SetBool : uint
22 {
23    unset, false, true;
24
25    /*public property bool     // NOT WORKING!
26    {
27       set { return value ? true : false; }
28       get { return (this == true); }
29    }*/
30 };
31
32
33 public class JSONParser
34 {
35 public:
36    File f;
37    char ch;
38
39    void SkipEmpty()
40    {
41       while(!f.Eof() && (!ch || ch == ' ' || ch == '\n' || ch == '\r' || ch == '\t'))
42       {
43          f.Getc(&ch);
44       }
45    }
46
47    JSONResult GetValue(Class type, DataValue value)
48    {
49       JSONResult result = syntaxError;
50       ch = 0;
51       SkipEmpty();
52       if(ch == '\"')
53       {
54          String string;
55          result = GetString(&string);
56          if(result)
57          {
58             Property prop;
59             if(type && (!strcmp(type.name, "String") || !strcmp(type.dataTypeString, "char *")))
60             {
61                value.p = string;
62             }
63             else if(type && (type.type == enumClass || type.type == unitClass))
64             {
65                if(((bool (*)(void *, void *, const char *))(void *)type._vTbl[__ecereVMethodID_class_OnGetDataFromString])(type, &value.i, string))
66                   result = success;
67                else
68                   result = typeMismatch;
69                delete string;
70             }
71             else if(type && (prop = eClass_FindProperty(type, "String", type.module)))
72             {
73                // TOFIX: Add more conversion property support... Expecting void * compatible here
74                value.p = ((void *(*)())(void *)prop.Set)(string);
75                result = success;
76                delete string;
77             }
78             else if(type && (type.type == structClass))
79             {
80                if(((bool (*)(void *, void *, const char *))(void *)type._vTbl[__ecereVMethodID_class_OnGetDataFromString])(type, value.p, string))
81                   result = success;
82                else
83                   result = typeMismatch;
84                delete string;
85             }
86             else
87             {
88                delete string;
89                result = typeMismatch;
90             }
91          }
92       }
93       else if(ch == '[')
94       {
95          Container array;
96          if(type && eClass_IsDerived(type, class(Map)))
97          {
98             result = GetMap(type, (Map *)&array);
99          }
100          else
101             result = GetArray(type, &array);
102
103          if(result == success && type && eClass_IsDerived(type, class(Container)))
104          {
105             value.p = array;
106          }
107          else
108          {
109             if(array)
110                array.Free();
111             delete array;
112             if(result != success)
113                result = typeMismatch;
114          }
115       }
116       else if(ch == '-' || isdigit(ch))
117       {
118          result = GetNumber(type, value);
119       }
120       else if(ch == '{')
121       {
122          void * object = value.p;
123          result = GetObject(type, &object);
124          if(result)
125          {
126             if(type && type.type == structClass);
127             else if(type && (type.type == normalClass || type.type == noHeadClass || type.type == bitClass))
128             {
129                value.p = object;
130             }
131             else
132             {
133                result = typeMismatch;
134                if(type)
135                   ((void (*)(void *, void *))(void *)type._vTbl[__ecereVMethodID_class_OnFree])(type, object);
136             }
137          }
138       }
139       else if(isalpha(ch))
140       {
141          char buffer[256];
142          int c = 0;
143          while(c < sizeof(buffer)-1 && isalpha(ch))
144          {
145             buffer[c++] = ch;
146             if(!f.Getc(&ch)) break;
147          }
148          buffer[c] = 0;
149          result = success;
150
151          if(type)
152          {
153             if(!strcmp(type.name, "bool"))
154             {
155                if(!strcmpi(buffer, "false")) value.i = 0;
156                else if(!strcmpi(buffer, "true")) value.i = 1;
157                else
158                   result = typeMismatch;
159             }
160             else if(!strcmp(type.name, "SetBool"))
161             {
162                if(!strcmpi(buffer, "false")) value.i = SetBool::false;
163                else if(!strcmpi(buffer, "true")) value.i = SetBool::true;
164                else
165                   result = typeMismatch;
166             }
167             else if(!strcmpi(buffer, "null"))
168             {
169                value.p = 0;
170             }
171             else
172                result = typeMismatch;
173          }
174          else
175             result = typeMismatch;
176       }
177       else if(ch == '}' || ch == ']')
178          result = noItem;
179       return result;
180    }
181
182    JSONResult GetArray(Class type, Container * array)
183    {
184       JSONResult result = syntaxError;
185       SkipEmpty();
186       *array = null;
187       if(ch == '[')
188       {
189          *array = eInstance_New(type);
190          result = success;
191          while(result)
192          {
193             DataValue value { };
194             Class arrayType = null;
195             JSONResult itemResult;
196
197             if(eClass_IsDerived(type, class(Container)))
198             {
199                arrayType = type.templateArgs[0].dataTypeClass;
200             }
201
202             itemResult = GetValue(arrayType, value);
203             if(itemResult == success)
204             {
205                // TODO: Verify the matching between template type and uint64
206                uint64 t;
207                if(arrayType.type == structClass)
208                {
209                   t = (uint64)(uintptr)value.p;
210                }
211                else if(arrayType == class(double) || !strcmp(arrayType.dataTypeString, "double"))
212                {
213                   t = value.ui64; //*(uint64 *)&value.d;
214                }
215                else if(arrayType == class(float) || !strcmp(arrayType.dataTypeString, "float"))
216                {
217                   t = value.ui; //f*(uint *)&value.f;
218                }
219                else if(arrayType.typeSize == sizeof(int64) || !strcmp(arrayType.dataTypeString, "int64") ||
220                   !strcmp(arrayType.dataTypeString, "unsigned int64") || !strcmp(arrayType.dataTypeString, "uint64"))
221                {
222                   t = value.ui64;
223                }
224                else if(arrayType.typeSize == sizeof(int) || !strcmp(arrayType.dataTypeString, "int") ||
225                   !strcmp(arrayType.dataTypeString, "unsigned int") || !strcmp(arrayType.dataTypeString, "uint"))
226                {
227                   t = value.i;
228                }
229                else if(arrayType.typeSize == sizeof(short int) || !strcmp(arrayType.dataTypeString, "short") ||
230                   !strcmp(arrayType.dataTypeString, "unsigned short") || !strcmp(arrayType.dataTypeString, "uint16") ||
231                   !strcmp(arrayType.dataTypeString, "int16"))
232                {
233                   t = value.s;
234                }
235                else if(arrayType.typeSize == sizeof(byte) || !strcmp(arrayType.dataTypeString, "char") ||
236                   !strcmp(arrayType.dataTypeString, "unsigned char") || !strcmp(arrayType.dataTypeString, "byte"))
237                {
238                   t = value.c;
239                }
240                else
241                {
242                   t = (uint64)(uintptr)value.p;
243                }
244                ((void *(*)(void *, uint64))(void *)array->Add)(*array, t);
245             }
246             else
247             {
248                if(itemResult == typeMismatch)
249                {
250                   if(arrayType)
251                      PrintLn("Warning: Incompatible value for array value, expected ", (String)arrayType.name);
252                }
253                else if(itemResult == noItem)
254                   result = success;
255                else
256                   result = itemResult;
257             }
258
259             if(result != syntaxError)
260             {
261                if(ch != ']' && ch != ',')
262                {
263                   ch = 0;
264                   SkipEmpty();
265                }
266                if(ch == ']')
267                {
268                   break;
269                }
270                else if(ch != ',')
271                   result = syntaxError;
272             }
273          }
274       }
275       ch = 0;
276       return result;
277    }
278
279    JSONResult GetMap(Class type, Map * map)
280    {
281       JSONResult result = syntaxError;
282       SkipEmpty();
283       *map = null;
284       if(ch == '[')
285       {
286          Class mapNodeType = type.templateArgs[0].dataTypeClass;
287          Class keyType = mapNodeType.templateArgs[0].dataTypeClass;
288          Property keyProp = null;
289          if(keyType && !strcmp(keyType.dataTypeString, "char *"))
290             keyProp = eClass_FindProperty(mapNodeType, "key", mapNodeType.module);
291
292          *map = eInstance_New(type);
293          result = success;
294
295          while(result)
296          {
297             DataValue value { };
298
299             JSONResult itemResult;
300
301             itemResult = GetValue(mapNodeType, value);
302             if(itemResult == success)
303             {
304                String s = keyProp ? ((void * (*)(void *))(void *)keyProp.Get)(value.p) : null;
305                ((void *(*)(void *, uint64))(void *)map->Add)(*map, (uint64)(uintptr)value.p);
306                // Must free String keys here
307                delete s;
308             }
309             else
310             {
311                if(itemResult == typeMismatch)
312                {
313                   if(mapNodeType)
314                      PrintLn("Warning: Incompatible value for array value, expected ", (String)mapNodeType.name);
315                }
316                else if(itemResult == noItem)
317                   result = success;
318                else
319                   result = itemResult;
320             }
321
322             if(result != syntaxError)
323             {
324                if(ch != ']' && ch != ',')
325                {
326                   ch = 0;
327                   SkipEmpty();
328                }
329                if(ch == ']')
330                {
331                   break;
332                }
333                else if(ch != ',')
334                   result = syntaxError;
335             }
336          }
337       }
338       ch = 0;
339       return result;
340    }
341
342    JSONResult GetString(String * string)
343    {
344       JSONResult result = syntaxError;
345       Array<char> buffer { minAllocSize = 256 };
346       bool escaped = false;
347
348       *string = null;
349       SkipEmpty();
350       if(ch == '\"')
351       {
352          while(f.Getc(&ch))
353          {
354             if(ch == '\\' && !escaped)
355                escaped = true;
356             else
357             {
358                if(escaped)
359                {
360                   if(ch == 'b') ch = '\b';
361                   else if(ch == 'f') ch = '\f';
362                   else if(ch == 'n') ch = '\n';
363                   else if(ch == 'r') ch = '\r';
364                   else if(ch == 't') ch = '\t';
365                   else if(ch == 'u')
366                   {
367                      // SKIP FOR NOW...
368                      char unicode[4];
369                      f.Getc(&unicode[0]);
370                      f.Getc(&unicode[1]);
371                      f.Getc(&unicode[2]);
372                      f.Getc(&unicode[3]);
373                   }
374                   escaped = false;
375                }
376                else if(ch == '\"')
377                {
378                   break;
379                }
380                buffer.Add(ch);
381                if(buffer.minAllocSize < buffer.count)
382                   buffer.minAllocSize *= 2;
383             }
384          }
385          buffer.Add(0);
386          *string = CopyString(buffer.array);
387          result = success;
388       }
389       delete buffer;
390       if(ch != ',' && ch != '}')
391          ch = 0;
392       return result;
393    }
394
395    public JSONResult GetObject(Class objectType, void ** object)
396    {
397       JSONResult result = syntaxError;
398       if(!objectType || objectType.type != structClass)
399          *object = null;
400       SkipEmpty();
401       if(ch == '{')
402       {
403          Class mapKeyClass = null, mapDataClass = null;
404
405          if(objectType && objectType.templateClass && eClass_IsDerived(objectType.templateClass, class(MapNode)))
406          {
407             mapKeyClass = objectType.templateArgs[0].dataTypeClass;
408             mapDataClass = objectType.templateArgs[2].dataTypeClass;
409          }
410
411          result = success;
412          if(objectType && (objectType.type == noHeadClass || objectType.type == normalClass))
413          {
414             *object = eInstance_New(objectType);
415          }
416          else if(objectType && objectType.type != structClass)
417          {
418             *object = eSystem_New(objectType.typeSize);
419          }
420
421          while(result)
422          {
423             String string;
424             ch = 0;
425             if(GetString(&string))
426             {
427                DataMember member = null;
428                Property prop = null;
429                Class type = null;
430                bool isKey = false;
431                uint offset = 0;
432
433                if(objectType)
434                {
435                   string[0] = (char)tolower(string[0]);
436                   if(mapKeyClass && !strcmp(string, "key"))
437                   {
438                      prop = eClass_FindProperty(objectType, "key", objectType.module);
439                      type = mapKeyClass;
440                      isKey = true;
441                   }
442                   else if(mapDataClass && !strcmp(string, "value"))
443                   {
444                      prop = eClass_FindProperty(objectType, "value", objectType.module);
445                      type = mapDataClass;
446                   }
447                   else
448                   {
449                      member = eClass_FindDataMember(objectType, string, objectType.module, null, null);
450                      if(member)
451                      {
452                         type = eSystem_FindClass(__thisModule, member.dataTypeString);
453                         if(!type)
454                            type = eSystem_FindClass(__thisModule.application, member.dataTypeString);
455
456                         offset = member._class.offset + member.offset;
457                      }
458                      else if(!member)
459                      {
460                         prop = eClass_FindProperty(objectType, string, objectType.module);
461                         if(prop)
462                         {
463                            type = eSystem_FindClass(__thisModule, prop.dataTypeString);
464                            if(!type)
465                               type = eSystem_FindClass(__thisModule.application, prop.dataTypeString);
466                         }
467                         else
468                            PrintLn("Warning: member ", string, " not found in class ", (String)objectType.name);
469                      }
470                   }
471                }
472                // Find Member in Object Class
473                {
474                   DataValue value { };
475
476                   if(type && type.type == structClass)
477                   {
478                      value.p = (byte *)*object + offset;
479                   }
480                   ch = 0;
481                   SkipEmpty();
482                   if(ch == ':')
483                   {
484                      JSONResult itemResult = GetValue(type, value);
485                      if(itemResult != syntaxError)
486                      {
487                         if(prop || member)
488                         {
489                            if(!type)
490                            {
491                               PrintLn("warning: Unresolved data type ", member ? (String)member.dataTypeString : (String)prop.dataTypeString);
492                            }
493                            else if(itemResult == success)
494                            {
495                               // Set value
496                               if(member)
497                               {
498                                  // TOFIX: How to swiftly handle classes with base data type?
499                                  if(type.type == structClass)
500                                     ;
501                                  else if(type.type == normalClass || type.type == noHeadClass)
502                                  {
503                                     void ** ptr = (void**)((byte *)*object + offset);
504                                     if(eClass_IsDerived(type, class(Container)) && *ptr)
505                                     {
506                                        Container container = (Container)*ptr;
507                                        container.Free();
508                                        delete container;
509                                     }
510                                     *ptr = value.p;
511                                  }
512                                  else if(type == class(double) || !strcmp(type.dataTypeString, "double"))
513                                  {
514                                     *(double *)((byte *)*object + offset) = value.d;
515                                  }
516                                  else if(type == class(float) || !strcmp(type.dataTypeString, "float"))
517                                  {
518                                     *(float *)((byte *)*object + offset) = value.f;
519                                  }
520                                  else if(type.typeSize == sizeof(int64) || !strcmp(type.dataTypeString, "int64") ||
521                                     !strcmp(type.dataTypeString, "unsigned int64") || !strcmp(type.dataTypeString, "uint64"))
522                                  {
523                                     *(uint64 *)((byte *)*object + offset) = value.ui64;
524                                  }
525                                  else if(type.typeSize == sizeof(int) || !strcmp(type.dataTypeString, "int") ||
526                                     !strcmp(type.dataTypeString, "unsigned int") || !strcmp(type.dataTypeString, "uint"))
527                                  {
528                                     *(int *)((byte *)*object + offset) = value.i;
529                                  }
530                                  else if(type.typeSize == sizeof(short int) || !strcmp(type.dataTypeString, "short") ||
531                                     !strcmp(type.dataTypeString, "unsigned short") || !strcmp(type.dataTypeString, "uint16") ||
532                                     !strcmp(type.dataTypeString, "int16"))
533                                  {
534                                     *(short *)((byte *)*object + offset) = value.s;
535                                  }
536                                  else if(type.typeSize == sizeof(byte) || !strcmp(type.dataTypeString, "char") ||
537                                     !strcmp(type.dataTypeString, "unsigned char") || !strcmp(type.dataTypeString, "byte"))
538                                  {
539                                     *(char *)((byte *)*object + offset) = value.c;
540                                  }
541                                  else
542                                  {
543                                     *(void **)((byte *)*object + offset) = value.p;
544                                  }
545                               }
546                               else if(prop && prop.Set)
547                               {
548                                  if(!strcmp(type.dataTypeString, "char *"))
549                                  {
550                                     ((void (*)(void *, void *))(void *)prop.Set)(*object, value.p);
551                                     if(!isKey)
552                                        delete value.p;
553                                  }
554                                  // TOFIX: How to swiftly handle classes with base data type?
555                                  else if(type == class(double) || !strcmp(type.dataTypeString, "double"))
556                                  {
557                                     ((void (*)(void *, double))(void *)prop.Set)(*object, value.d);
558                                  }
559                                  else if(type == class(float) || !strcmp(type.dataTypeString, "float"))
560                                  {
561                                     ((void (*)(void *, float))(void *)prop.Set)(*object, value.f);
562                                  }
563                                  else if(type.typeSize == sizeof(int64) || !strcmp(type.dataTypeString, "int64") ||
564                                     !strcmp(type.dataTypeString, "unsigned int64") || !strcmp(type.dataTypeString, "uint64"))
565                                  {
566                                     ((void (*)(void *, uint64))(void *)prop.Set)(*object, value.ui64);
567                                  }
568                                  else if(type.typeSize == sizeof(int) || !strcmp(type.dataTypeString, "int") ||
569                                     !strcmp(type.dataTypeString, "unsigned int") || !strcmp(type.dataTypeString, "uint"))
570                                  {
571                                     ((void (*)(void *, int))(void *)prop.Set)(*object, value.i);
572                                  }
573                                  else if(type.typeSize == sizeof(short int) || !strcmp(type.dataTypeString, "short") ||
574                                     !strcmp(type.dataTypeString, "unsigned short") || !strcmp(type.dataTypeString, "uint16") ||
575                                     !strcmp(type.dataTypeString, "int16"))
576                                  {
577                                     ((void (*)(void *, short))(void *)prop.Set)(*object, value.s);
578                                  }
579                                  else if(type.typeSize == sizeof(byte) || !strcmp(type.dataTypeString, "char") ||
580                                     !strcmp(type.dataTypeString, "unsigned char") || !strcmp(type.dataTypeString, "byte"))
581                                  {
582                                     ((void (*)(void *, char))(void *)prop.Set)(*object, value.c);
583                                  }
584                                  else
585                                  {
586                                     ((void (*)(void *, void *))(void *)prop.Set)(*object, value.p);
587                                  }
588                               }
589                            }
590                            else
591                            {
592                               PrintLn("Warning: Incompatible value for ", member ? (String)member.name : (String)prop.name,
593                                  ", expected ", member ? (String)member.dataTypeString : (String)prop.dataTypeString);
594                            }
595                         }
596                      }
597                   }
598                   else
599                      result = syntaxError;
600                }
601             }
602             else if(ch && ch != '}' && ch != ',')
603                result = syntaxError;
604             delete string;
605
606             if(result)
607             {
608                SkipEmpty();
609                if(ch == '}')
610                {
611                   break;
612                }
613                else if(ch != ',')
614                   result = syntaxError;
615             }
616          }
617       }
618       ch = 0;
619       return result;
620    }
621
622    JSONResult GetNumber(Class type, DataValue value)
623    {
624       JSONResult result = syntaxError;
625       char buffer[256];
626       int c = 0;
627       while(c < sizeof(buffer)-1 && (ch == '-' || ch == '.' || tolower(ch) == 'e' || ch == '+' || isdigit(ch)))
628       {
629          buffer[c++] = ch;
630          if(!f.Getc(&ch)) break;
631       }
632       buffer[c] = 0;
633       //if(strchr(buffer, '.'))
634
635       // TOFIX: How to swiftly handle classes with base data type?
636       if(type == class(double) || !strcmp(type.dataTypeString, "double"))
637       {
638          value.d = strtod(buffer, null);
639          result = success;
640       }
641       else if(type == class(float) || !strcmp(type.dataTypeString, "float"))
642       {
643          value.f = (float)strtod(buffer, null);
644          result = success;
645       }
646       // TOFIX: int64 looks for class long long?
647       //else if(type == class(int64) || !strcmp(type.dataTypeString, "int64"))
648       else if(!strcmp(type.dataTypeString, "int64"))
649       {
650          value.i64 = strtol(buffer, null, 10);  // TOFIX: 64 bit support
651          result = success;
652       }
653       else if(type == class(uint64) || !strcmp(type.dataTypeString, "uint64"))
654       {
655          value.ui64 = strtol(buffer, null, 10);  // TOFIX: 64 bit support
656          result = success;
657       }
658       else
659       {
660          value.i = strtol(buffer, null, 10);
661          result = success;
662       }
663       return result;
664    }
665 }
666
667 bool WriteMap(File f, Class type, Map map, int indent)
668 {
669    if(map)
670    {
671       int i;
672       bool isFirst = true;
673       MapIterator it { map = map };
674       Class mapNodeClass = map._class.templateArgs[0].dataTypeClass;
675       f.Puts("[\n");
676       indent++;
677
678       while(it.Next())
679       {
680          MapNode n = (MapNode)it.pointer;
681          if(!isFirst)
682             f.Puts(",\n");
683          else
684             isFirst = false;
685          for(i = 0; i<indent; i++) f.Puts("   ");
686          _WriteJSONObject(f, mapNodeClass, n, indent);
687       }
688       f.Puts("\n");
689       indent--;
690       for(i = 0; i<indent; i++) f.Puts("   ");
691       f.Puts("]");
692    }
693    else
694       f.Puts("null");
695    return true;
696 }
697
698 bool WriteArray(File f, Class type, Container array, int indent)
699 {
700    if(array)
701    {
702       int i;
703       bool isFirst = true;
704       Iterator it { array };
705       Class arrayType = type.templateArgs[0].dataTypeClass;
706       f.Puts("[\n");
707       indent++;
708
709       while(it.Next())
710       {
711          DataValue value { };
712          uint64 t = ((uint64(*)(void *, void *))(void *)array.GetData)(array, it.pointer);
713          if(!isFirst)
714             f.Puts(",\n");
715          else
716             isFirst = false;
717
718          // Add value
719          // TODO: Verify the matching between template type and uint64
720          if(arrayType.type == structClass)
721          {
722             value.p = (void *)(uintptr)t;
723          }
724          else if(arrayType == class(double) || !strcmp(arrayType.dataTypeString, "double"))
725          {
726             value.ui64 = t;
727             //value.d = *(double *)&t;
728          }
729          else if(arrayType == class(float) || !strcmp(arrayType.dataTypeString, "float"))
730          {
731             value.ui = (uint)t;
732             //value.f = *(float *)&t;
733          }
734          else if(arrayType.typeSize == sizeof(int64) || !strcmp(arrayType.dataTypeString, "int64") ||
735             !strcmp(arrayType.dataTypeString, "unsigned int64") || !strcmp(arrayType.dataTypeString, "uint64"))
736          {
737             value.ui64 = t;
738          }
739          else if(arrayType.typeSize == sizeof(int) || !strcmp(arrayType.dataTypeString, "int") ||
740             !strcmp(arrayType.dataTypeString, "unsigned int") || !strcmp(arrayType.dataTypeString, "uint"))
741          {
742             value.i = (int)t;
743          }
744          else if(arrayType.typeSize == sizeof(short int) || !strcmp(arrayType.dataTypeString, "short") ||
745             !strcmp(arrayType.dataTypeString, "unsigned short") || !strcmp(arrayType.dataTypeString, "uint16") ||
746             !strcmp(arrayType.dataTypeString, "int16"))
747          {
748             value.s = (short)t;
749          }
750          else if(arrayType.typeSize == sizeof(byte) || !strcmp(arrayType.dataTypeString, "char") ||
751             !strcmp(arrayType.dataTypeString, "unsigned char") || !strcmp(arrayType.dataTypeString, "byte"))
752          {
753             value.c = (char)t;
754          }
755          else
756          {
757             value.p = (void *)(uintptr)t;
758          }
759          for(i = 0; i<indent; i++) f.Puts("   ");
760          WriteValue(f, arrayType, value, indent);
761       }
762       f.Puts("\n");
763       indent--;
764       for(i = 0; i<indent; i++) f.Puts("   ");
765       f.Puts("]");
766    }
767    else
768       f.Puts("null");
769    return true;
770 }
771
772 bool WriteNumber(File f, Class type, DataValue value, int indent)
773 {
774    char buffer[1024];
775    bool needClass = false;
776    buffer[0] = 0;
777    if(type == class(double) || !strcmp(type.dataTypeString, "double"))
778       ((const char *(*)(void *, void *, char *, void *, bool *))(void *)type._vTbl[__ecereVMethodID_class_OnGetString])(type, &value.d, buffer, 0, &needClass);
779    else if(type == class(float) || !strcmp(type.dataTypeString, "float"))
780       ((const char *(*)(void *, void *, char *, void *, bool *))(void *)type._vTbl[__ecereVMethodID_class_OnGetString])(type, &value.f, buffer, null, &needClass);
781    else if(!strcmp(type.dataTypeString, "int64"))
782       ((const char *(*)(void *, void *, char *, void *, bool *))(void *)type._vTbl[__ecereVMethodID_class_OnGetString])(type, &value.i64, buffer, null, &needClass);
783    else if(!strcmp(type.dataTypeString, "unsigned int64") || !strcmp(type.dataTypeString, "uint64") || type.typeSize == sizeof(int64))
784       ((const char *(*)(void *, void *, char *, void *, bool *))(void *)type._vTbl[__ecereVMethodID_class_OnGetString])(type, &value.ui64, buffer, null, &needClass);
785    else if(!strcmp(type.dataTypeString, "int"))
786       ((const char *(*)(void *, void *, char *, void *, bool *))(void *)type._vTbl[__ecereVMethodID_class_OnGetString])(type, &value.i, buffer, null, &needClass);
787    else if(!strcmp(type.dataTypeString, "unsigned int") || !strcmp(type.dataTypeString, "uint") || type.typeSize == sizeof(int))
788       ((const char *(*)(void *, void *, char *, void *, bool *))(void *)type._vTbl[__ecereVMethodID_class_OnGetString])(type, &value.ui, buffer, null, &needClass);
789    else if(!strcmp(type.dataTypeString, "short") || !strcmp(type.dataTypeString, "int16"))
790       ((const char *(*)(void *, void *, char *, void *, bool *))(void *)type._vTbl[__ecereVMethodID_class_OnGetString])(type, &value.s, buffer, null, &needClass);
791    else if(!strcmp(type.dataTypeString, "unsigned short") || !strcmp(type.dataTypeString, "uint16") || type.typeSize == sizeof(short int))
792       ((const char *(*)(void *, void *, char *, void *, bool *))(void *)type._vTbl[__ecereVMethodID_class_OnGetString])(type, &value.us, buffer, null, &needClass);
793    else if(!strcmp(type.dataTypeString, "char"))
794       ((const char *(*)(void *, void *, char *, void *, bool *))(void *)type._vTbl[__ecereVMethodID_class_OnGetString])(type, &value.c, buffer, null, &needClass);
795    else if(!strcmp(type.dataTypeString, "unsigned char") || !strcmp(type.dataTypeString, "byte") || type.typeSize == sizeof(byte))
796       ((const char *(*)(void *, void *, char *, void *, bool *))(void *)type._vTbl[__ecereVMethodID_class_OnGetString])(type, &value.uc, buffer, null, &needClass);
797    f.Puts(buffer);
798    return true;
799 }
800
801 bool WriteValue(File f, Class type, DataValue value, int indent)
802 {
803    if(!strcmp(type.name, "String") || !strcmp(type.dataTypeString, "char *"))
804    {
805       if(!value.p)
806          f.Puts("null");
807       else
808       {
809          f.Puts("\"");
810          //if(strchr(value.p, '\"') || strchr(value.p, '\\'))
811          {
812             int c = 0;
813             int b = 0;
814             char buffer[1024];
815             char * string = value.p;
816             char ch;
817             while(true)
818             {
819                ch = string[c++];
820                if(ch == '\"')
821                {
822                   buffer[b] = 0;
823                   f.Puts(buffer);
824                   f.Puts("\\\"");
825                   b = 0;
826                }
827                else if(ch == '\\')
828                {
829                   buffer[b] = 0;
830                   f.Puts(buffer);
831                   f.Puts("\\\\");
832                   b = 0;
833                }
834                else if(b == sizeof(buffer)-2 || !ch)
835                {
836                   buffer[b++] = ch;
837                   if(ch) buffer[b] = 0;
838                   f.Puts(buffer);
839                   b = 0;
840                   if(!ch) break;
841                }
842                else
843                   buffer[b++] = ch;
844             }
845          }
846          /*else
847             f.Puts(value.p);*/
848          f.Puts("\"");
849       }
850    }
851    else if(!strcmp(type.name, "bool"))
852    {
853       if(value.i)
854          f.Puts("true");
855       else
856          f.Puts("false");
857    }
858    else if(!strcmp(type.name, "SetBool"))
859    {
860       if(value.i == SetBool::true)
861          f.Puts("true");
862       else if(value.i == SetBool::false)
863          f.Puts("false");
864       else
865          f.Puts("unset");
866    }
867    else if(type.type == enumClass || type.type == unitClass)
868    {
869       f.Puts("\"");
870       WriteNumber(f, type, value, indent);
871       f.Puts("\"");
872    }
873    else if(eClass_IsDerived(type, class(Map)))
874    {
875       WriteMap(f, type, value.p, indent);
876    }
877    else if(eClass_IsDerived(type, class(Container)))
878    {
879       WriteArray(f, type, value.p, indent);
880    }
881    else if(type.type == normalClass || type.type == noHeadClass || type.type == structClass)
882    {
883       _WriteJSONObject(f, type, value.p, indent);
884    }
885    else if(type.type == bitClass)
886    {
887       Class dataType;
888       dataType = eSystem_FindClass(__thisModule, type.dataTypeString);
889       WriteNumber(f, dataType, value, indent);
890    }
891    else if(type.type == systemClass)
892    {
893       WriteNumber(f, type, value, indent);
894    }
895    return true;
896 }
897
898 public bool WriteJSONObject(File f, Class objectType, void * object, int indent)
899 {
900    bool result = false;
901    if(object)
902    {
903       result = _WriteJSONObject(f, objectType, object, indent);
904       f.Puts("\n");
905    }
906    return result;
907 }
908
909 static bool _WriteJSONObject(File f, Class objectType, void * object, int indent)
910 {
911    if(object)
912    {
913       const char * string = null;
914
915       if(objectType._vTbl[__ecereVMethodID_class_OnGetString] != objectType.base._vTbl[__ecereVMethodID_class_OnGetString])
916       {
917          char buffer[1024];
918          buffer[0] = 0;
919          string = ((const char *(*)())(void *)objectType._vTbl[__ecereVMethodID_class_OnGetString])(objectType, object, buffer, null, null);
920       }
921       if(string)
922       {
923          // TOCHECK: ProjectNode.ec why do we add quotes in OnGetString there?
924          if(string[0] == '\"')
925             f.Puts(string);
926          else
927          {
928             f.Puts("\"");
929             f.Puts(string);
930             f.Puts("\"");
931          }
932       }
933       else
934       {
935          Property prop;
936          int c;
937          bool isFirst = true;
938          Class mapKeyClass = null, mapDataClass = null;
939          Class baseClass;
940          List<Class> bases { };
941
942          if(objectType.templateClass && eClass_IsDerived(objectType.templateClass, class(MapNode)))
943          {
944             mapKeyClass = objectType.templateArgs[0].dataTypeClass;
945             mapDataClass = objectType.templateArgs[2].dataTypeClass;
946          }
947
948          f.Puts("{\n");
949          indent++;
950
951          for(baseClass = objectType; baseClass; baseClass = baseClass.base)
952          {
953             if(baseClass.isInstanceClass || !baseClass.base)
954                break;
955             bases.Insert(null, baseClass);
956          }
957
958          for(baseClass : bases)
959          {
960             for(prop = baseClass.membersAndProperties.first; prop; prop = prop.next)
961             {
962                if(prop.memberAccess != publicAccess || (prop.isProperty && (!prop.Set || !prop.Get))) continue;
963                if(prop.isProperty)
964                {
965                   if(!prop.conversion && (!prop.IsSet || prop.IsSet(object)))
966                   {
967                      DataValue value { };
968                      Class type;
969
970                      if(mapKeyClass && !strcmp(prop.name, "key"))
971                         type = mapKeyClass;
972                      else if(mapDataClass && !strcmp(prop.name, "value"))
973                         type = mapDataClass;
974                      else
975                         type = eSystem_FindClass(__thisModule, prop.dataTypeString);
976                      if(!type)
977                         type = eSystem_FindClass(__thisModule.application, prop.dataTypeString);
978                      if(!type)
979                         PrintLn("warning: Unresolved data type ", (String)prop.dataTypeString);
980                      else
981                      {
982                         // TOFIX: How to swiftly handle classes with base data type?
983                         if(type == class(double) || !strcmp(type.dataTypeString, "double"))
984                         {
985                            value.d = ((double (*)(void *))(void *)prop.Get)(object);
986                         }
987                         else if(type == class(float) || !strcmp(type.dataTypeString, "float"))
988                         {
989                            value.f = ((float (*)(void *))(void *)prop.Get)(object);
990                         }
991                         else if(type.typeSize == sizeof(int64) || !strcmp(type.dataTypeString, "int64") ||
992                            !strcmp(type.dataTypeString, "unsigned int64") || !strcmp(type.dataTypeString, "uint64"))
993                         {
994                            value.ui64 = ((uint64 (*)(void *))(void *)prop.Get)(object);
995                         }
996                         else if(type.typeSize == sizeof(int) || !strcmp(type.dataTypeString, "int") ||
997                            !strcmp(type.dataTypeString, "unsigned int") || !strcmp(type.dataTypeString, "uint"))
998                         {
999                            value.i = ((int (*)(void *))(void *)prop.Get)(object);
1000                         }
1001                         else if(type.typeSize == sizeof(short int) || !strcmp(type.dataTypeString, "short") ||
1002                            !strcmp(type.dataTypeString, "unsigned short") || !strcmp(type.dataTypeString, "uint16") ||
1003                            !strcmp(type.dataTypeString, "int16"))
1004                         {
1005                            value.s = ((short (*)(void *))(void *)prop.Get)(object);
1006                         }
1007                         else if(type.typeSize == sizeof(byte) || !strcmp(type.dataTypeString, "char") ||
1008                            !strcmp(type.dataTypeString, "unsigned char") || !strcmp(type.dataTypeString, "byte"))
1009                         {
1010                            value.c = ((char (*)(void *))(void *)prop.Get)(object);
1011                         }
1012                         else if(type.type == structClass)
1013                         {
1014                            value.p = new byte[type.structSize];
1015                            ((void (*)(void *, void *))(void *)prop.Get)(object, value.p);
1016                         }
1017                         else
1018                         {
1019                            value.p = ((void *(*)(void *))(void *)prop.Get)(object);
1020                         }
1021
1022                         if(!isFirst) f.Puts(",\n");
1023                         for(c = 0; c<indent; c++) f.Puts("   ");
1024
1025                         f.Puts("\"");
1026                         f.Putc((char)toupper(prop.name[0]));
1027                         f.Puts(prop.name+1);
1028                         f.Puts("\" : ");
1029                         WriteValue(f, type, value, indent);
1030                         isFirst = false;
1031                         if(type.type == structClass)
1032                            delete value.p;
1033                      }
1034                   }
1035                }
1036                else
1037                {
1038                   DataMember member = (DataMember)prop;
1039                   DataValue value { };
1040                   uint offset;
1041                   Class type = eSystem_FindClass(__thisModule, member.dataTypeString);
1042                   if(!type)
1043                      type = eSystem_FindClass(__thisModule.application, member.dataTypeString);
1044
1045                   offset = member._class.offset + member.offset;
1046
1047                   if(type)
1048                   {
1049                      if(type.type == normalClass || type.type == noHeadClass || type.type == structClass || !strcmp(type.name, "String"))
1050                      {
1051                         if(type.type == structClass)
1052                            value.p = (void *)((byte *)object + offset);
1053                         else
1054                            value.p = *(void **)((byte *)object + offset);
1055                         if(!value.p)
1056                            continue;
1057                      }
1058                      else if(type == class(double) || !strcmp(type.dataTypeString, "double"))
1059                      {
1060                         value.d = *(double *)((byte *)object + offset);
1061                      }
1062                      else if(type == class(float) || !strcmp(type.dataTypeString, "float"))
1063                      {
1064                         value.f = *(float *)((byte *)object + offset);
1065                      }
1066                      else if(type.typeSize == sizeof(int64) || !strcmp(type.dataTypeString, "int64") ||
1067                         !strcmp(type.dataTypeString, "unsigned int64") || !strcmp(type.dataTypeString, "uint64"))
1068                      {
1069                         value.ui64 = *(uint64 *)((byte *)object + offset);
1070                      }
1071                      else if(type.typeSize == sizeof(int) || !strcmp(type.dataTypeString, "int") ||
1072                         !strcmp(type.dataTypeString, "unsigned int") || !strcmp(type.dataTypeString, "uint"))
1073                      {
1074                         value.i = *(int *)((byte *)object + offset);
1075                         if(!strcmp(type.name, "bool") || type.type == enumClass)
1076                            if(!value.i)
1077                               continue;
1078                      }
1079                      else if(type.typeSize == sizeof(short int) || !strcmp(type.dataTypeString, "short") ||
1080                         !strcmp(type.dataTypeString, "unsigned short") || !strcmp(type.dataTypeString, "uint16") ||
1081                         !strcmp(type.dataTypeString, "int16"))
1082                      {
1083                         value.s = *(short *)((byte *)object + offset);
1084                      }
1085                      else if(type.typeSize == sizeof(byte) || !strcmp(type.dataTypeString, "char") ||
1086                         !strcmp(type.dataTypeString, "unsigned char") || !strcmp(type.dataTypeString, "byte"))
1087                      {
1088                         value.c = *(char *)((byte *)object + offset);
1089                      }
1090                      else
1091                      {
1092                         value.i = *(int *)((byte *)object + offset);
1093                      }
1094
1095                      if(!isFirst) f.Puts(",\n");
1096                      for(c = 0; c<indent; c++) f.Puts("   ");
1097
1098                      f.Puts("\"");
1099                      f.Putc((char)toupper(member.name[0]));
1100                      f.Puts(member.name+1);
1101                      f.Puts("\" : ");
1102                      WriteValue(f, type, value, indent);
1103                      isFirst = false;
1104                   }
1105                }
1106             }
1107          }
1108
1109          delete bases;
1110
1111          indent--;
1112          f.Puts("\n");
1113          for(c = 0; c<indent; c++) f.Puts("   "); f.Puts("}");
1114       }
1115    }
1116    else
1117       f.Puts("null");
1118    return true;
1119 }