我很难超越自定义jsonconverter的写入方法,以稍微改变执行序列化的方式。
我需要调用接受具有通用部分的某些输入的REST服务。我可以重现以下有效载荷格式的问题:
public sealed class JsonField
{
public string Key { get; set; }
public object Value { get; set; }
public string OtherParam { get; set; }
}
public sealed class JsonPayload
{
public string Item1 { get; set; }
public string Item2 { get; set; }
public List<JsonField> Fields { get; set; }
}
我称之为的REST API需要将字段是一个对象,其中包含与原始集合中指定的密钥属性相对应的名称的多个字段。喜欢:
{
"Item1" : "Value1",
"Item2" : "Value2",
...
"Fields":
{
"Key1": {"Value":"Value1", "OtherParam":"other1"}
"Key2": {"Value":42, "OtherParam":"other2"}
}
}
但是,使用默认选项,有效载荷被序列化:
{
"Item1" : "Value1",
"Item2" : "Value2",
...
"Fields":[
{ "Key":"Key1", "Value":"Value1", "OtherParam":"other1" }
{ "Key":"Key2", "Value":42, "OtherParam":"other2" }
]
}
您注意到有一个我想要一个对象的对象集合。另外,我希望关键名称是字段中各个属性的名称。
我很难弄清楚如何在自定义转换器中使用Jtoken,Jproperty,Jvalue对象。实际上,我从来没有尝试过这样的事情,所以我很难把头缠绕在这些概念上。
我尝试过创建两个自定义转换器,一个在类范围内,以防止收集的生成,第二个是在集合范围的,但到目前为止却没有成功。
这是我尝试过的:
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
var token = JToken.FromObject(value);
var key = token["Key"];
if (key == null)
throw new Exception("missing key");
var propertyName = key.ToString();
var json = new StringBuilder();
json.Append("{");
foreach (var child in token.Children())
{
var property = child as JProperty;
if (property == null || property.Name == "Key")
continue;
var propName = property.Name;
var propValue = JsonConvert.SerializeObject(property.Value);
json.AppendFormat(""{0}": {1},", propName, propValue);
}
if (json.Length > 1)
json.Remove(json.Length - 1, 1);
json.Append("}");
var newToken = JToken.Parse(json.ToString());
var serializedObject = JsonConvert.SerializeObject(newToken);
writer.WriteStartObject();
writer.WritePropertyName(propertyName);
writer.WriteToken(newToken.CreateReader());
writer.WriteEndObject();
}
有没有办法执行我想实现的目标?
也许我以错误的方式处理问题,所以请,如果您有更轻松的选择,那么一定要毫不犹豫地分享您的想法。
您在正确的轨道上。您在此处只需要一个转换器 - 对于JsonField
对象列表,代码实际上非常简单。这就是您所需要的:
class JsonFieldListConverter : JsonConverter
{
public override bool CanConvert(Type objectType)
{
return (objectType == typeof(List<JsonField>));
}
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
JObject containerObj = new JObject();
foreach (JsonField field in (List<JsonField>)value)
{
JObject itemObj = new JObject();
itemObj.Add("Value", JToken.FromObject(field.Value));
itemObj.Add("OtherParam", new JValue(field.OtherParam));
containerObj.Add(field.Key, itemObj);
}
containerObj.WriteTo(writer);
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
throw new NotImplementedException();
}
}
这是一个演示,显示了动作中的转换器:
class Program
{
static void Main(string[] args)
{
JsonPayload payload = new JsonPayload
{
Item1 = "Value1",
Item2 = "Value2",
Fields = new List<JsonField>
{
new JsonField { Key = "Key1", Value = "Value1", OtherParam = "other1" },
new JsonField { Key = "Key2", Value = 42, OtherParam = "other2" },
}
};
JsonSerializerSettings settings = new JsonSerializerSettings();
settings.Converters.Add(new JsonFieldListConverter());
settings.Formatting = Formatting.Indented;
string json = JsonConvert.SerializeObject(payload, settings);
Console.WriteLine(json);
}
}
输出:
{
"Item1": "Value1",
"Item2": "Value2",
"Fields": {
"Key1": {
"Value": "Value1",
"OtherParam": "other1"
},
"Key2": {
"Value": 42,
"OtherParam": "other2"
}
}
}
简短答案:
您可以通过将List<T>
更改为Dictionary<T>
以示例为例:
您只需通过将List<T>
更改为Dictionary<T>
来做到这一点,这将为您提供似乎是单个对象,其中包含属性名称作为字典的键。如果类包含键,尽管您可以[JsonIgnore]
,但这也将被序列化。
void Main()
{
var pl = new JsonPayload()
{Item1 = "Value1",Item2 = "Value2"};
var field1 = new JsonField() { Key = "Key1", Value = "Value1", OtherParam = "other1" };
var field2 = new JsonField() { Key = "Key2", Value = "Value2", OtherParam = "other2" };
pl.Fields = new Dictionary<string, JsonField>() { { field1.Key , field1}, { field2.Key, field2 }};
string json = JsonConvert.SerializeObject(pl);
JsonPayload pl2 = JsonConvert.DeserializeObject<JsonPayload>(json);
string output = JsonConvert.SerializeObject(pl2);
output.Dump();
}
public sealed class JsonField
{
public string Key { get; set; }
public object Value { get; set; }
public string OtherParam { get; set; }
}
public sealed class JsonPayload
{
public string Item1 { get; set; }
public string Item2 { get; set; }
public Dictionary<string, JsonField> Fields { get; set; }
}