I’m after feedback on some code for unmarshalling xml data into a struct, where the field names match the xml element names.
After quite a bit of fiddling, my tests are passing, it all seems to work fine… but is it appropriate to use an inline loop, std.meta.fieldNames & the @field builtin in this way? Is there a more idiomatic approach I should be taking for this kind of task?
pub const Metadata = struct {
dbname: ?[]const u8 = null,
dataOwner: ?[]const u8 = null,
dataOriginTimespan: ?[]const u8 = null,
// snipped - lots more fields
fn init(self: *Metadata, alloc: Allocator, root: xml.xmlNodePtr) !xml.xmlNodePtr {
var curr = xml.xmlFirstElementChild(root);
while (curr != null and !std.mem.eql(u8, std.mem.span(curr.*.name), "schemas")) : (curr = xml.xmlNextElementSibling(curr)) {
const value = try alloc.dupe(u8, std.mem.trim(u8, std.mem.span(xml.xmlNodeGetContent(curr)), " \t\r\n"));
inline for (comptime std.meta.fieldNames(@TypeOf(self.*))) |nm| {
if (std.mem.eql(u8, nm, std.mem.span(curr.*.name))) {
@field(self, nm) = value;
break;
}
} else alloc.free(value);
}
return curr;
}
fn deinit(self: *Metadata, alloc: Allocator) void {
inline for (comptime std.meta.fieldNames(@TypeOf(self.*))) |nm| {
if (@field(self, nm)) |v| alloc.free(v);
}
}
};
test "metadata" {
const example =
\\ <?xml version="1.0" encoding="UTF-8" standalone="no"?>
\\ <?xml-stylesheet type="text/xsl" href="metadata.xsl"?><siardArchive xmlns="http://www.bar.admin.ch/xmlns/siard/1.0/metadata.xsd" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" version="1.0" xsi:schemaLocation="http://www.bar.admin.ch/xmlns/siard/1.0/metadata.xsd metadata.xsd">
\\ <dbname>testnt</dbname>
\\ <dataOwner>(...)</dataOwner>
\\ <dataOriginTimespan>2015</dataOriginTimespan>
\\ <!-- snipped -->
\\ <schemas/>
\\ </siardArchive>
;
const d = xml.xmlReadMemory(example.ptr, @intCast(example.len), null, "utf-8", xml.XML_PARSE_NOBLANKS | xml.XML_PARSE_RECOVER | xml.XML_PARSE_NOERROR | xml.XML_PARSE_NOWARNING);
const root = xml.xmlDocGetRootElement(d);
var m = Metadata{};
const schema_ptr = try m.init(std.testing.allocator, root);
try std.testing.expect(schema_ptr != null);
try std.testing.expect(m.databaseProduct != null);
try std.testing.expectEqualStrings(m.databaseProduct.?, "Microsoft SQL Server 12.00.2000");
m.deinit(std.testing.allocator);
}