JsonConverter Keyvalues with =而不是:separator

时间:2018-01-15 17:07:20

标签: c# serialization json.net key-value splunk

通常我通过Newtonsoft JsonConvert.SerializeObject() 记录我的对象数据,该序列化为对象内容的字符串。

但不是将KeyValue对分开:我希望它们被=分隔,以便被Splunk记录。

所以不要喜欢

{ "Name": "John", Age : 18 } 

我想要

{ "Name" = "John", Age = 18 }

当然我也在寻找适用于对象内部对象的东西。从API看起来有点僵硬,以不同于格式化分隔符:因为它在一天结束时不是JSON格式。

有什么简单的方法可以做到这一点,而不是重新发明轮子?

1 个答案:

答案 0 :(得分:2)

这应该适用于Json.NET,但不适用于自定义JsonConverter。 Newtonsoft保持其序列化器之间的区别,它递归地遍历ac#对象图发出抽象列表和包含键/值对的对象,以及它的 writer ,它们格式化并输出结果根据某些特定标准的序列化。您可以利用这种区别来创建自己的JsonWriter自定义子类,以Splunk所需的方式格式化键和值。

话虽如此,您的问题仍然存在不一致:引用"Name"属性名称,但不引用Age属性名称。如果你想要引用特定的规则来引用某些属性名称而不引用其他属性名称,我看不到用Json.NET做到这一点的简单方法。但是,在Logging best practices Splunk写道:

  

Splunk软件最强大的功能之一是它能够在您搜索时从事件中提取字段,从非结构化数据中创建结构。要确保字段提取按预期工作,请使用以下字符串语法(使用空格和逗号很好):

     

key1=value1, key2=value2, key3=value3 . . .

     

如果您的值包含空格,请将它们用引号括起来(例如,username="bob smith")。

因此,似乎不需要选择性引用属性名称。

假设是这种情况,在创建自定义JsonWriter时,您可以使用以下编写器作为模型,两者均由Newtonsoft的James Newton-King创建:

基于这两个类,我提出了SplunkLogTextWriter的部分实现,它以您所需的格式生成输出:

public class SplunkLogTextWriter : JsonWriter
{
    readonly TextWriter _writer;

    public bool QuotePropertyNames { get; set; }

    public SplunkLogTextWriter(TextWriter textWriter)
    {
        if (textWriter == null)
        {
            throw new ArgumentNullException();
        }

        _writer = textWriter;
    }

    public override void Flush()
    {
        _writer.Flush();
    }

    public override void Close()
    {
        base.Close();
        if (CloseOutput)
            _writer.Close();
    }

    public override void WriteComment(string text)
    {
        base.WriteComment(text);
        throw new NotImplementedException();
    }

    public override void WriteRaw(string json)
    {
        base.WriteRaw(json);

        _writer.Write(json);
    }

    public override void WriteStartArray()
    {
        base.WriteStartArray();
        _writer.Write("[ ");
    }

    public override void WriteStartObject()
    {
        base.WriteStartObject();
        _writer.Write("{ ");
    }

    public override void WriteStartConstructor(string name)
    {
        base.WriteStartConstructor(name);
        throw new NotImplementedException();
    }

    protected override void WriteEnd(JsonToken token)
    {
        base.WriteEnd(token);
        switch (token)
        {
            case JsonToken.EndObject:
                _writer.Write(" }");
                break;
            case JsonToken.EndArray:
                _writer.Write(" ]");
                break;
            case JsonToken.EndConstructor:
                _writer.Write(" )");
                break;
            default:
                throw new JsonWriterException("Invalid JsonToken: " + token);
        }
    }

    public override void WritePropertyName(string name)
    {
        base.WritePropertyName(name);

        WriteEscapedString(name, QuotePropertyNames);

        _writer.Write(" = ");
    }

    void WriteEscapedString(string s, bool quote)
    {
        s = s ?? string.Empty;
        quote = quote || s.Length == 0 || s.Any(c => Char.IsWhiteSpace(c) || c == '=' || c == ']' || c == '}');

        if (quote)
            _writer.Write('"');

        foreach (var c in s)
        {
            switch (c)
            {
                case '\t':
                    _writer.Write(@"\t");
                    break;
                case '\n':
                    _writer.Write(@"\n");
                    break;
                case '\r':
                    _writer.Write(@"\r");
                    break;
                case '\f':
                    _writer.Write(@"\f");
                    break;
                case '\b':
                    _writer.Write(@"\b");
                    break;
                case '\\':
                    _writer.Write(@"\\");
                    break;
                case '\u0085': // Next Line
                    _writer.Write(@"\u0085");
                    break;
                case '\u2028': // Line Separator
                    _writer.Write(@"\u2028");
                    break;
                case '\u2029': // Paragraph Separator
                    _writer.Write(@"\u2029");
                    break;
                default:
                    _writer.Write(c);
                    break;
            }
        }

        if (quote)
            _writer.Write('"');
    }

    #region WriteValue methods

    public override void WriteNull()
    {
        base.WriteNull();
        _writer.Write("null");
    }

    public override void WriteValue(bool value)
    {
        base.WriteValue(value);
        _writer.Write(value ? "true" : "false");
    }

    public override void WriteValue(byte value)
    {
        base.WriteValue(value);
        WriteIntegerValue((ulong)value);
    }

    public override void WriteValue(byte[] value)
    {
        base.WriteValue(value);
        if (value == null)
            _writer.Write("null");
        else
            WriteEscapedString(Convert.ToBase64String(value), false);
    }

    public override void WriteValue(sbyte value)
    {
        base.WriteValue(value);
        WriteIntegerValue(value);
    }

    public override void WriteValue(char value)
    {
        base.WriteValue(value);
        WriteEscapedString(value.ToString(), false);
    }

    public override void WriteValue(short value)
    {
        base.WriteValue(value);
        WriteIntegerValue(value);
    }

    public override void WriteValue(ushort value)
    {
        base.WriteValue(value);
        WriteIntegerValue((ulong)value);
    }

    public override void WriteValue(int value)
    {
        base.WriteValue(value);
        WriteIntegerValue(value);
    }

    public override void WriteValue(uint value)
    {
        base.WriteValue(value);
        WriteIntegerValue((ulong)value);
    }

    public override void WriteValue(long value)
    {
        base.WriteValue(value);
        WriteIntegerValue(value);
    }

    public override void WriteValue(ulong value)
    {
        base.WriteValue(value);
        WriteIntegerValue(value);
    }

    private void WriteIntegerValue(long value)
    {
        _writer.Write(value.ToString(Culture ?? CultureInfo.InvariantCulture));
    }

    private void WriteIntegerValue(ulong value)
    {
        _writer.Write(value.ToString(Culture ?? CultureInfo.InvariantCulture));
    }

    public override void WriteValue(DateTime value)
    {
        base.WriteValue(value);

        // Use Json.NET to format the date.  Consider replacing with something more performant.
        var s = JsonConvert.ToString(value, DateFormatHandling, DateTimeZoneHandling);
        WriteEscapedString(s.Substring(1, s.Length - 2), false);
    }

    public override void WriteValue(DateTimeOffset value)
    {
        base.WriteValue(value);

        // Use Json.NET to format the date.  Consider replacing with something more performant.
        var s = JsonConvert.ToString(value, DateFormatHandling);
        WriteEscapedString(s.Substring(1, s.Length - 2), false);
    }

    public override void WriteValue(decimal value)
    {
        base.WriteValue(value);
        _writer.Write(value.ToString(Culture ?? CultureInfo.InvariantCulture));
    }

    public override void WriteValue(double value)
    {
        base.WriteValue(value);
        // JsonConvert.ToString(value, FloatFormatHandling, QuoteChar, false) -- not public
        _writer.Write(JsonConvert.ToString(value));
    }

    public override void WriteValue(string value)
    {
        base.WriteValue(value);
        WriteEscapedString(value, false);
    }

    protected override void WriteValueDelimiter()
    {
        base.WriteValueDelimiter();
        _writer.Write(", ");
    }

    public override void WriteUndefined()
    {
        base.WriteUndefined();
        throw new NotImplementedException();
    }

    public override void WriteValue(Guid value)
    {
        base.WriteValue(value);
        throw new NotImplementedException();
    }

    #endregion

    // ToDo
    // WriteIndentSpace()
    // WriteIndent()
    // async methods
    // Others for which I threw a NotImplementedException()
}

然后,如果您按如下方式使用它:

var root = new { Name = "John", Age = 18 };

var sb = new StringBuilder();
using (var sw = new StringWriter(sb))
using (var writer = new SplunkLogTextWriter(sw))
{
    JsonSerializer.CreateDefault().Serialize(writer, root);
}

Console.WriteLine(sb);

生成以下结果:

{ Name = John, Age = 18 }

请注意,编写器的某些部分尚未实现。查找ToDo条评论和NotImplementedException投注。考虑到日志格式的正式定义,您还需要调整编写器,您的问题没有提供。

示例fiddle