自定义 JSON 衍生格式

Custom JSON Derivative Format

我想要一个与 JSON 几乎相同的序列化格式,除了键值表示为 <key>="<value>" 而不是 "<key>":"<value>".

我用 Newtonsoft 制作了一个名为 TsonConverter 的自定义 JsonConverter,效果相当好,只是它不能 "see" 嵌入字典。给定以下类型:

public class TraceyData
{
    [Safe]
    public string Application { get; set; }

    [Safe]
    public string SessionID { get; set; }
    [Safe]
    public string TraceID { get; set; }
    [Safe]
    public string Workflow { get; set; }

    [Safe]
    public Dictionary<string, string> Tags {get; set; }

    [Safe]
    public string[] Stuff {get; set;} 
}

以及以下代码:

TsonConverter weird = new TsonConverter();
JsonSerializerSettings settings = new JsonSerializerSettings();
settings.NullValueHandling = NullValueHandling.Ignore;
settings.Converters.Add(weird);

var tracey = new TraceyData();
tracey.TraceID = Guid.NewGuid().ToString();
tracey.SessionID = "5";
tracey.Tags["Referrer"] = "http://www.sky.net/deals";
tracey.Stuff = new string[] { "Alpha", "Bravo", "Charlie" };
tracey.Application = "Responsive";

string  stuff = JsonConvert.SerializeObject(tracey, settings);

我明白了:

[Application="Responsive" SessionID="5" TraceID="082ef853-92f8-4ce8-9f32-8e4f792fb022" Tags={"Referrer":"http://www.sky.net/deals"} Stuff=["Alpha","Bravo","Charlie"]]

显然我也覆盖了 StartObject/EndObject 符号,将 { } 替换为 [ ]。否则结果还不错。

但是内部字典的问题还是存在的。为了 要将字典也转换为使用我的 <key>="<value>" 格式,看来我必须制作一个 deep dictionary converter.

我想知道是否有更简单的方法来做到这一点。

也许 Newtonsoft 工具有一个 "property generator" 和 "key-value" 生成器 属性,我可以设置它来全局处理这个问题?

有什么建议吗?

虽然我们在这里,但我想知道是否有一个 StartObject/EndObject 格式化程序 属性 覆盖我可以设置,它将处理我上面显示的其他自定义。 "skip" 为这些简单的更改制作 JsonConverter 工具会很好。

顺便提一句:

附录

下面是我制作的TraceConverter。它引用了一个 FieldMetaData class,它只包含 属性 信息。

public class TsonConverter : JsonConverter
{
    public override bool CanRead
    {
        get
        {
            return false;
        }
    }

    public override bool CanConvert(Type ObjectType)
    {
        return DataClassifier.TestForUserType(ObjectType);
    }

    public override void WriteJson(
        JsonWriter writer, object value, JsonSerializer serializer)
    {
        Type objType = value.GetType();
        var props = objType.GetProperties(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
        var propMap = from p in props
                        from a in p.GetCustomAttributes(typeof(ProfileAttribute), false)
                        select new FieldMetaData(p, (ProfileAttribute)a);

        //writer.WriteStartObject();
        writer.WriteStartArray();
        bool loopStarted = true;
        foreach(var prop in propMap){
            object rawValue = prop.GetValue(value);
            if (rawValue != null || serializer.NullValueHandling == NullValueHandling.Include)
            {
                string jsonValue = JsonConvert.SerializeObject(prop.GetValue(value), this);
                if (loopStarted)
                {
                    loopStarted = false;
                    writer.WriteRaw(String.Format("{0}={1}", prop.Name, jsonValue));
                }
                else
                {
                    writer.WriteRaw(String.Format(" {0}={1}", prop.Name, jsonValue));
                }
            }
            //writer.WriteRaw(String.Format("{0}={1}", prop.Name, prop.GetValue(value)));
            //writer.WritePropertyName(prop.Name, false);
            //writer.WriteValue(prop.GetValue(value));
        }
        writer.WriteEndArray();
    }

    public override object ReadJson(
        JsonReader reader, Type objectType,
        object existingValue, JsonSerializer serializer)
    {
        throw new NotImplementedException();
    }

}

与其创建自己的转换器,不如创建自己的 JsonWriter that writes to your custom file format. (This is how Json.NET implements its BsonWriter.) In your case, your file format is close enough to JSON that you can inherit from JsonTextWriter:

子类
public class TsonTextWriter : JsonTextWriter
{
    TextWriter _writer;

    public TsonTextWriter(TextWriter textWriter)
        : base(textWriter)
    {
        if (textWriter == null)
            throw new ArgumentNullException("textWriter"); 
        QuoteName = false;
        _writer = textWriter;
    }

    public override void WriteStartObject()
    {
        SetWriteState(JsonToken.StartObject, null);

        _writer.Write('[');
    }

    protected override void WriteEnd(JsonToken token)
    {
        switch (token)
        {
            case JsonToken.EndObject:
                _writer.Write(']');
                break;
            default:
                base.WriteEnd(token);
                break;
        }
    }

    public override void WritePropertyName(string name)
    {
        WritePropertyName(name, true);
    }

    public override void WritePropertyName(string name, bool escape)
    {
        SetWriteState(JsonToken.PropertyName, name);

        var escaped = name;
        if (escape)
        {
            escaped = JsonConvert.ToString(name, '"', StringEscapeHandling);
            escaped = escaped.Substring(1, escaped.Length - 2);
        }

        // Maybe also escape the space character if it appears in a name?
        _writer.Write(escaped.Replace("=", @"\u003d"));// Replace "=" with unicode escape sequence.

        _writer.Write('=');
    }

    /// <summary>
    /// Writes the JSON value delimiter.  (Remove this override if you want to retain the comma separator.)
    /// </summary>
    protected override void WriteValueDelimiter()
    {
        _writer.Write(' ');
    }

    /// <summary>
    /// Writes an indent space.
    /// </summary>
    protected override void WriteIndentSpace()
    {
        // Do nothing.
    }
}

完成此操作后,现在所有 类 将在您使用此编写器时序列化为您的自定义格式,例如:

        var tracey = new TraceyData();
        tracey.TraceID = Guid.NewGuid().ToString();
        tracey.SessionID = "5";
        tracey.Tags["Referrer"] = "http://www.sky.net/deals";
        tracey.Stuff = new string[] { "Alpha", "Bravo", "Charlie" };
        tracey.Application = "Responsive";

        JsonSerializerSettings settings = new JsonSerializerSettings();
        settings.NullValueHandling = NullValueHandling.Ignore; 

        using (var sw = new StringWriter())
        {
            using (var jsonWriter = new TsonTextWriter(sw))
            {
                JsonSerializer.CreateDefault(settings).Serialize(jsonWriter, tracey);
            }
            Debug.WriteLine(sw.ToString());
        }

产生输出

[Application="Responsive" SessionID="5" TraceID="2437fe67-9788-47ba-91ce-2e5b670c2a34" Tags=[Referrer="http://www.sky.net/deals"] Stuff=["Alpha" "Bravo" "Charlie"]]

至于根据 [Safe] 属性的存在来决定是否序列化属性,这是第二个问题。您需要创建自己的 ContractResolver and override CreateProperty, for instance as shown here: Using JSON.net, how do I prevent serializing properties of a derived class, when used in a base class context?

更新

如果要保留数组而不是对象的逗号分隔符,请按如下方式修改WriteValueDelimiter

    /// <summary>
    /// Writes the JSON value delimiter.  (Remove this override if you want to retain the comma separator.)
    /// </summary>
    protected override void WriteValueDelimiter()
    {
        if (WriteState == WriteState.Array)
            _writer.Write(',');
        else
            _writer.Write(' ');
    }