自定义 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 工具会很好。
顺便提一句:
- 我的自定义 JsonConverter 正在根据示例中显示的
[Safe]
属性选择要序列化的属性。这是另一个不错的选择。如果 JSon 设置可以公开一个 "attribute handler" 属性 让我覆盖通常的 JSon 属性以支持我自己的属性,那就太好了。
- 我不需要反序列化这种格式。它旨在作为一种单向操作。如果有人也想解释如何反序列化我的自定义格式,那是一个有趣的奖励,但绝对没有必要回答这个问题。
附录
下面是我制作的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(' ');
}
我想要一个与 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 工具会很好。
顺便提一句:
- 我的自定义 JsonConverter 正在根据示例中显示的
[Safe]
属性选择要序列化的属性。这是另一个不错的选择。如果 JSon 设置可以公开一个 "attribute handler" 属性 让我覆盖通常的 JSon 属性以支持我自己的属性,那就太好了。 - 我不需要反序列化这种格式。它旨在作为一种单向操作。如果有人也想解释如何反序列化我的自定义格式,那是一个有趣的奖励,但绝对没有必要回答这个问题。
附录
下面是我制作的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(' ');
}