From 3032957071294928d25e2d7497e3ad35dc176145 Mon Sep 17 00:00:00 2001 From: Dendy Date: Sun, 23 Oct 2022 23:24:28 +0200 Subject: [PATCH] Detect tag ausence when parsing JSON to Item --- src/Item.zig | 37 ++++++++++++++++++++++++------------- src/json.zig | 46 ++++++++++++++++++++++++++++++++++++++++------ src/main.zig | 14 +++++++++++++- 3 files changed, 77 insertions(+), 20 deletions(-) diff --git a/src/Item.zig b/src/Item.zig index 9ef8f76..4beff6a 100644 --- a/src/Item.zig +++ b/src/Item.zig @@ -65,11 +65,11 @@ pub fn toJson(self: Self) json.Obj { // Add id (i64|null) jobj.objectAdd("id", if (self.id) |id| &json.Obj.newInt64(id) else null); - var jtags = json.Obj.newObject(); - jobj.objectAdd("tags", &jtags); - // Add tags only if they're initialized if (self.tags) |tags| { + var jtags = json.Obj.newObject(); + jobj.objectAdd("tags", &jtags); + for (tags) |tag_i| { const jtag = if (tag_i.value) |value| &json.Obj.newString(value) @@ -78,26 +78,37 @@ pub fn toJson(self: Self) json.Obj { jtags.objectAdd(tag_i.name, jtag); } + } else { + // If nothing is found, set it to null + // just to make it more explicit + jobj.objectAdd("tags", null); } return jobj; } pub fn fromJson(jobj: json.Obj, allocator: std.mem.Allocator) !Self { - var jtags = jobj.objectGet("tags"); - defer jtags.deinit(); + // Try to assemble a Item object from a JSON string + // An item could be just an id, just tags or both. - const len = @intCast(usize, jtags.objectLen()); - var tags = try allocator.alloc(Tag, len); + var opt_jtags = jobj.objectGet("tags") catch null; + var tags: ?[]Tag = null; - var iter = jtags.objectGetIterator(); + if (opt_jtags) |*jtags| { + defer jtags.deinit(); + const len = @intCast(usize, jtags.objectLen()); + tags = try allocator.alloc(Tag, len); - var i: usize = 0; - while (iter.next()) |*tag| { - tags[i].name = try allocator.dupeZ(u8, tag.key); - tags[i].value = if (tag.value) |*value| try allocator.dupeZ(u8, value.getString()) else null; + var iter = jtags.objectGetIterator(); - i += 1; + var i: usize = 0; + while (iter.next()) |*tag| { + // Whe know it's not null at this point + tags.?[i].name = try allocator.dupeZ(u8, tag.key); + tags.?[i].value = if (tag.value) |*value| try allocator.dupeZ(u8, value.getString()) else null; + + i += 1; + } } return Self{ diff --git a/src/json.zig b/src/json.zig index 29f7a56..45fa35e 100644 --- a/src/json.zig +++ b/src/json.zig @@ -1,10 +1,21 @@ -const c = @cImport({ +pub const c = @cImport({ @cInclude("json.h"); }); const std = @import("std"); +const Type = enum(c.json_type) { + @"null" = c.json_type_null, + boolean = c.json_type_boolean, + double = c.json_type_double, + int = c.json_type_int, + object = c.json_type_object, + array = c.json_type_array, + string = c.json_type_string, +}; + pub const Obj = struct { - obj: *c.json_object, + // Option because a null value is treated as the C value NULL in json-c + obj: ?*c.json_object, // TODO: Port type attribute to a zig enum and assign it upon creation @@ -31,6 +42,15 @@ pub const Obj = struct { return Obj{ .obj = c.json_tokener_parse(s).? }; } + const TypeError = error { + TypeMismatch, // Tried to use a function on the wrong JSON format + }; + + pub fn getType(self: Obj) Type { + //std.debug.print("{}\n", .{c.json_object_get_type(self.obj)}); + return @intToEnum(Type, c.json_object_get_type(self.obj)); + } + // Not *const Obj because the json_object retains ownership of the string pub fn getString(self: *Obj) []const u8 { // TODO: Check type to not allow other types @@ -51,13 +71,27 @@ pub const Obj = struct { // ** Object functions ** ///////////////////////////////// - // TODO: Create an error for type checking. + const GetError = error { + TypeMismatch, // Not an object/array + KeyNotFound, // Key or ID not present in the object/array + }; - pub fn objectGet(self: *const Obj, key: [*c]const u8) Obj { + pub fn objectGet(self: *const Obj, key: [*c]const u8) GetError!Obj { // TODO: Check type and null return as errors var obj: ?*c.json_object = undefined; - _ = c.json_object_object_get_ex(self.obj, key, &obj); - return Obj{ .obj = c.json_object_get(obj).? }; + const succ = c.json_object_object_get_ex(self.obj, key, &obj); + + // Everything went alright + if (obj) |ret| { + return Obj{ .obj = c.json_object_get(ret).? }; + } else if (obj == null and succ != 0){ + // The actual value is just null + return Obj{ .obj = null }; + } else if (self.getType() != Type.object){ + return GetError.TypeMismatch; + } else { + return GetError.KeyNotFound; + } } pub fn objectLen(self: *const Obj) c_int { diff --git a/src/main.zig b/src/main.zig index 1a9fd7c..588bd49 100644 --- a/src/main.zig +++ b/src/main.zig @@ -31,7 +31,19 @@ pub fn main() !void { try item.persist(&db); - var jobj = item.toJson(); + // ---------- + + const jsonText2: [:0]const u8 = + \\{ "id" : 1 } + ; + + var jsonItem2 = json.Obj.newFromString(jsonText2); + defer jsonItem2.deinit(); + + var item2 = try Item.fromJson(jsonItem2, allocator); + defer item2.deinit(); + + var jobj = item2.toJson(); defer jobj.deinit(); std.debug.print("{s}\n", .{jobj.toString()});