A KV1 document with several sibling top-level objects — normal in Valve's own script files (scripts/soundscapes_.txt, game_sounds_.txt) — is parsed as if the first object were the document root. Its name is dropped, its children are hoisted to the top level, and the remaining siblings are appended alongside them.
const string Doc = """
"first"
{
"a" "1"
"b" "2"
}
"second"
{
"c" "3"
}
"third"
{
"d" "4"
}
""";
var serializer = KVSerializer.Create(KVSerializationFormat.KeyValues1Text);
using var stream = new MemoryStream(Encoding.UTF8.GetBytes(Doc));
KVObject kv = serializer.Deserialize(stream);
Console.WriteLine(string.Join(", ", kv.Select(c => c.Key)));
Expected: first, second, third
Actual: a, b, second, third
Production file with this 'bug': Half-Life Alyx/game/hlvr/pak01_dir.vpk:scripts/soundscapes_a1.txt
Workaround: wrap the raw text in a synthetic root before parsing ("__root" { … }), which returns the expected 29.
Version: ValveKeyValue 0.70.0.499, KVSerializationFormat.KeyValues1Text
A KV1 document with several sibling top-level objects — normal in Valve's own script files (scripts/soundscapes_.txt, game_sounds_.txt) — is parsed as if the first object were the document root. Its name is dropped, its children are hoisted to the top level, and the remaining siblings are appended alongside them.
Expected:
first, second, thirdActual:
a, b, second, thirdProduction file with this 'bug':
Half-Life Alyx/game/hlvr/pak01_dir.vpk:scripts/soundscapes_a1.txtWorkaround: wrap the raw text in a synthetic root before parsing ("__root" { … }), which returns the expected 29.
Version:
ValveKeyValue 0.70.0.499, KVSerializationFormat.KeyValues1Text