【问题标题】:Custom JSON Derivative Format自定义 JSON 衍生格式
【发布时间】:2015-06-03 03:12:13
【问题描述】:

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

使用 Newtonsoft,我制作了一个名为 TsonConverter 的自定义 JsonConverter,它工作得相当好,只是它不能“看到”嵌入式字典。给定以下类型:

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" "查理"]]

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

但是,内部字典仍然存在问题。为了 要将字典也转换为使用我的&lt;key&gt;="&lt;value&gt;" 格式,看来我必须创建一个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();
    }

}

【问题讨论】:

  • 发布您的 jsonconverter 类并寻找其他实现 blog.maskalik.com/asp-net/…
  • 示例输出中的属性之间没有逗号分隔符,但是在您的初始段落中,您没有指定应删除逗号。它应该存在吗?

标签: c# .net json json.net


【解决方案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(' ');
    }

【讨论】:

    猜你喜欢
    • 2014-12-26
    • 2013-04-22
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-08-12
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多