#include <stdio.h>
#include <stdlib.h> //malloc
#include <ctype.h> //isspace
void element(FILE* input, FILE* output);
char* key(FILE* input);
void array(FILE* input, FILE* output);
int string(FILE* input, FILE* output);
int main(int argc, char** argv) {
if (argc < 3) {
printf("Must pass JSON input file and XML output file as arguments in that order.\n");
return 1;
}
//create a new json file, filter out all whitespace
FILE* original_json = fopen(argv[1], "r");
FILE* new_json = fopen("./temp.json", "w");
int c;
while ((c = fgetc(original_json)) != EOF) {
if(isspace(c)) continue;
fputc(c, new_json);
}
fputc('\n', new_json);
fclose(original_json);
fclose(new_json);
//set pointer to input and output files
FILE* json = fopen("./temp.json", "r");
FILE* xml = fopen(argv[2], "w");
//check entire file for an unnamed object
int unnamed_obj = 0; //default to not having an unnamed object
int steps = 0;
for (c = fgetc(json); c != EOF; c = fgetc(json)) {
//only first object can be unnamed so just look for first '{'
if (c == '{') {
//if first char is '{' it must be unnamed
if (steps == 0) {
unnamed_obj = 1;
break;
}
fseek(json, -2, SEEK_CUR); //put cursor behind the '{'
c = fgetc(json);
unnamed_obj = (c != ':'); //turn on unnamed object if the first '{' is named
break;
}
steps++;
}
fseek(json, 0, SEEK_SET); //put cursor back at beginning of file
if (unnamed_obj) fputs("<xml>\n", xml); //print root if unnamed object exists
//go through file and look for '[' (array) or ':' (key:value)
int found_data = 0;
for (c = fgetc(json); c != EOF; c = fgetc(json)) {
switch (c) {
case '[':
found_data = 1;
fputs("<ul>\n", xml);
array(json, xml);
fputs("\n</ul>\n", xml);
break;
case ':':
found_data = 1;
fseek(json, -1, SEEK_CUR); //element expects pointer at the ':' so move it back one
element(json, xml);
}
}
//print the unnamed root if there is an unnamed object
if (unnamed_obj) fputs("\n</xml>\n", xml);
//puts null if no data was found
if(!found_data) {
freopen(argv[2], "w", xml);
fputs("<null />\n", xml);
}
fclose(json);
fclose(xml);
remove("./temp.json");
return 0;
}
void element(FILE* input, FILE* output) {
char* key_name = key(input); //if file pointer does not point to ':' this won't work
fputs("\n<", output);
fputs(key_name, output);
fputc('>', output);
for (int c = fgetc(input); (c != ',') && (c != '}'); c = fgetc(input)) {
switch (c) {
case '[':
fputs("\n<ul>\n", output);
array(input, output);
fputs("\n</ul>\n", output);
break;
case '{':
while ((c = fgetc(input)) != ':');
element(input, output);
break;
case '"':
if (string(input, output)) element(input, output);
break;
}
if (c>=0x2D && c<=0x39) {
fputc(c, output);
}
if (c>=0x61 && c<=0x7A) {
fputc('<', output);
do {
fputc(c, output);
c = fgetc(input);
} while (c>=0x61 && c<=0x7A);
fputs(" />", output);
fseek(input, -1, SEEK_CUR);
}
}
fputs("</", output);
fputs(key_name, output);
fputs(">\n", output);
free(key_name);
return;
}
char* key(FILE* input) {
fseek(input, -3, SEEK_CUR);
unsigned long string_len = 1;
int key_char;
while ((key_char = fgetc(input)) != '"') {
fseek(input, -2, SEEK_CUR);
string_len++;
}
char *string_out = malloc(string_len+1);
int char_pos = 0;
while ((key_char = fgetc(input)) != '"') {
if (key_char == '\\') {
fseek(input, 1, SEEK_CUR); //increment pointer to ignore whatever is after the escape sequence
continue;
}
string_out[char_pos] = (char)key_char;
char_pos++;
}
string_out[char_pos] = (char)0;
//if we landed on ':' increment an extra step so that
//it effectively undoes the next instruction because
//we want to end on the char after ':'
if (fgetc(input) == ':') fseek(input, 1, SEEK_CUR);
//if we landed on '}' we want to step back to stay on
//'}' so when we return the program will scan the '}' and exit
fseek(input, -1, SEEK_CUR);
return string_out;
}
void array(FILE* input, FILE* output) {
char* open_tag = "<li>";
char* close_tag = "</li>";
int c;
while ((c = fgetc(input)) != ']') {
switch (c) {
case ',':
fputs(close_tag, output);
fputs(open_tag, output);
break;
case '[':
fputs("\n<ul>\n", output);
array(input, output);
fputs("\n</ul>\n", output);
break;
case '{':
fputs(open_tag, output);
//if there is a nested object, find the : and call element function
while ((c = fgetc(input)) != '}') {
if (c == ':') {
fseek(input, -1, SEEK_CUR);
element(input, output);
fseek(input, -1, SEEK_CUR);
}
}
fputs(close_tag, output);
break;
case '"':
fputs(open_tag, output);
string(input, output);
fputs(close_tag, output);
}
if (c>=0x2D && c<=0x39) {
fputs(open_tag, output);
do {
fputc(c, output);
} while (((c = fgetc(input)) != ',') && (c != ']'));
fputs(close_tag, output);
if (c == ']') fseek(input, -1, SEEK_CUR);
}
if (c>=0x61 && c<=0x7A) {
fputc('<', output);
do {
fputc(c, output);
c = fgetc(input);
} while (c>=0x61 && c<=0x7A);
fputs(" />", output);
fseek(input, -1, SEEK_CUR);
}
}
return;
}
int string(FILE* input, FILE* output) {
int c;
//set pointer to the end of the string
while ((c = fgetc(input)) != '"') {
if (c == '\\') {
fseek(input, 1, SEEK_CUR); //increment pointer to ignore whatever is after the escape sequence
continue;
}
}
//check if char after string is ':'
if (fgetc(input) == ':') {
fseek(input, -1, SEEK_CUR); //put pointer back to the ':'
return 1; //tell caller that the '"' signifies a key instead of a string
}
//print xml's string stuff then call key function to print string
fputs("<![CDATA[", output);
fputs(key(input), output);
fputs("]]>", output);
return 0;
}