自定义JSON衍生格式

时间:2015-06-03 03:12:13

标签: c# .net json json.net

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

使用Newtonsoft,我制作了一个名为JsonConverter的自定义TsonConverter,效果相当不错,但它无法“看到”嵌入式字典。给出以下类型:

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”,“喝彩”, “查理”]]

显然我也覆盖了StartObject / EndObject表示法,用{]替换{}。否则结果不错。

但是,仍然存在内部字典的问题。为了 要转换字典以使用我的<key>="<value>"格式,看起来我必须制作deep dictionary converter

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

也许Newtonsoft工具有一个“属性生成器”和“键值”生成器属性,我可以设置为全局处理这个属性吗?

有什么建议吗?

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

顺便提及:

  • 我的自定义JsonConverter正在根据我的示例中显示的[Safe]属性选择要序列化的属性。这是另一个不错的选择。如果JSon设置可以公开一个“属性处理程序”属性,让我可以覆盖我自己的常用JSon属性,这将是非常好的。
  • 我无需对此格式进行反序列化。它旨在作为单向操作。如果有人也希望解释如何反序列化我的自定义格式,这是一个有趣的奖励,但绝对没有必要回答这个问题。

附录

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

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();
    }

}

1 个答案:

答案 0 :(得分:1)

您不需要创建自己的转换器,而是需要创建自己的JsonWriter子类来写入自定义文件格式。 (这就是Json.NET实现其BsonWriter的方式。)在您的情况下,您的文件格式足够接近JSON,您可以从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并覆盖CreateProperty,例如: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(' ');
    }