Compact (Canonical) Serialized Form #565
Replies: 5 comments 8 replies
|
This sounds like a great thing to put together! I'll note that you shouldn't restrict node children in the ways you're thinking, as ordering matters in many cases, and even duplicates are perfectly legitimate. As far as other compactness measures: converting multi line strings to regular strings might be a good idea since they tend to take up a lot of unneeded whitespace. For type annotations I don't think there's much you need to do besides not having unnecessary whitespace and doing the usual string-to-identifier transform. Then there's small details like being explicit that the last child should not have a semicolon. Also that curly braces should be flush with surrounding tokens ( Thinking about it I actually kinda wonder if we can get more compact than JSON because there's fewer required separators in KDL |
Annotations are part of the data model and necessary to retain. There's no default annotations or anything, so it's quite straightforward - it a node or value has an annotation, preserve it. As Kat said, they're just strings, so they serialize with the compact string rules (prefer idents, etc). |
|
I think it would make sense to create a separate sub-spec for JiK, since in JiK
|
|
i done something like this for both testing and fun while i was implementing my own library to just save the documents i needed later, and it's much simpler than making a proper formatter. first of all, i want to disagree about making this as a sub-spec for jik or xik. if you want json or xml, then just use these languages. i think it should be a requirement that a single canonical compact form exists for any valid kdl document. you have a kdl document? then this form exists, and you can get it with a command like as a second point, my opinion is that this form should be also a valid kdl document and have the same semantics as the original document. no separate specification to implement - just kdl. and you can get the same values from reading this document back. and as the last requirement, i think this form should be a single line. all valid kdl documents are able to fit in a single line, and this allows usage, for example, in logging: one line in a file is one entry in the log. this gives a funny and actually great side-effect that the file from these lines is also a valid kdl document, because the newlines separate the last node on the previous line from the next. therefore, this form comes mostly to these statements (i hope i didn't forget anything):
we cannot do much more than that, because kdl is a node-based language and every node counts as unique, and their order also matters. the same goes for arguments. properties, however, are a special case, because the specification says that latter properties override the former. and to keep the result deterministic, they should be sorted and placed in a fixed place like before the arguments. but there is still a matter of values: strings, numbers and others. booleans and other keywords are simple: they are kept as is. for numbers and strings the situation is a bit more complex. i advocate that numbers should also be kept as-is. while strings, on the other hand, are able to be "compressed". to keep them in a single line, all strings should be converted to identifier, single-line or raw single-line strings (all strings can be represented in at least one of these forms). if string is able to be represented as an identifier (not looks like a keyword or a number, no spaces, etc.), then it is done so. all other strings are single-line quoted (not raw) strings. it can be argued that raw strings can be more compact - it is true: strings with many quotes and escapes. however, not all strings can be put in a raw string as-is: it requires the implementation to count the needed amount of hashes. i think it's more pragmatic to keep only quoted strings and keep the implementation trivial. if that goes (not essentially in this form) into the set of official specifications, then i will probably pretty easy make an implementation and a program for this (but no promises about the date). i reserve the |
|
I wrote a basic formatter utilising kdl-rs which passes a simple test but is still unused in my application so hasn't been thoroughly tested. Most of the complicated formatting is simply delegated to kdl-rs. I would have liked to test equality of the original vs compacted form after being deserialized but kdl-rs compares formatting unhelpfully (effectively Detailsuse std::{
fmt,
cmp::Ordering,
};
#[derive(Debug, Clone)]
pub struct Compact<'a> {
pub document: &'a kdl::KdlDocument,
}
impl<'a> Compact<'a> {
pub fn new(document: &'a kdl::KdlDocument) -> Self {
Self {
document,
}
}
}
impl<'a> fmt::Display for Compact<'a> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
format_document(self.document, f)
}
}
pub fn format_document(
document: &kdl::KdlDocument,
f: &mut fmt::Formatter,
) -> fmt::Result {
let len = document.nodes().len();
for (index, node) in document.nodes().iter().enumerate() {
format_node(node, f)?;
if index + 1 < len {
f.write_str(";")?;
}
}
Ok(())
}
pub fn format_node(
node: &kdl::KdlNode,
f: &mut fmt::Formatter,
) -> fmt::Result {
if let Some(type_identifier) = node.ty() {
format_type_identifier(type_identifier, f)?;
}
format_identifier(node.name(), f)?;
let mut entries = Vec::from(node.entries());
entries.sort_by(|a, b| {
match (a.name(), b.name()) {
// Preserve ordering
(None, None) => Ordering::Equal,
(Some(_), None) => Ordering::Greater,
(None, Some(_)) => Ordering::Less,
(Some(a_name), Some(b_name)) => {
a_name.value().cmp(b_name.value())
},
}
});
for entry in entries {
f.write_str(" ")?;
if let Some(name) = entry.name() {
format_identifier(name, f)?;
f.write_str("=")?;
}
if let Some(type_identifier) = entry.ty() {
format_type_identifier(type_identifier, f)?;
}
fmt::Display::fmt(entry.value(), f)?;
}
if let Some(children) = node.children() {
f.write_str("{")?;
format_document(children, f)?;
f.write_str("}")?;
}
Ok(())
}
pub fn format_type_identifier(
type_identifier: &kdl::KdlIdentifier,
f: &mut fmt::Formatter,
) -> fmt::Result {
f.write_str("(")?;
format_identifier(type_identifier, f)?;
f.write_str(")")?;
Ok(())
}
pub fn format_identifier(
identifier: &kdl::KdlIdentifier,
f: &mut fmt::Formatter,
) -> fmt::Result {
fmt::Display::fmt(&kdl::KdlValue::String(identifier.value().to_owned()), f)
} |
Uh oh!
There was an error while loading. Please reload this page.
There exists a specification RFC 8785 for serializing JSON in a compact and consistent way. This is useful for reproducability in cryptographic applications (e.g. hashing, signing). It may be useful to design such a standard for KDL documents as well.
I am starting a discussion to collect thoughts and help inform the design for the implementation I plan to use in my project. This may not end up being made into an official specification, but opinions from KDL experts may be useful for my project. For context, I will be using kdl-rs and its public API.
My initial ideas are this:
+or_.Unanswered questions:
Rules for compact serialization could be independent from the rules of a canonical ordering, and form two different specifications. A canonical ordering used without compact serialization could simply be a recommended (auto) format for KDL documents.
All reactions