Detect tag ausence when parsing JSON to Item

This commit is contained in:
Dendy 2022-10-23 23:24:28 +02:00
parent b8908c7b22
commit 3032957071
3 changed files with 77 additions and 20 deletions

View File

@ -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{

View File

@ -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 {

View File

@ -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()});