使用Newtonsoft JsonConvert序列化时将非对象值替换为类型名
解决方案
你可以通过两种方式实现需求:从.NET对象直接生成类型名结构的JSON,或者从现有JSON字符串转换得到目标结果。
场景1:从.NET对象生成类型名JSON
自定义一个JsonConverter,在序列化时将每个属性值替换为对应的类型名称,同时递归处理对象和数组结构:
using Newtonsoft.Json; using System; using System.Collections; using System.Reflection; public class TypeNameJsonConverter : JsonConverter { public override bool CanConvert(Type objectType) => true; public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) { if (value == null) { writer.WriteNull(); return; } Type type = value.GetType(); // 处理值类型与字符串 if (type.IsValueType || type == typeof(string)) { string typeName = type.Name; // 映射特殊类型名称 if (type == typeof(Guid)) typeName = "Guid"; else if (type == typeof(DateTimeOffset)) typeName = "DateTimeOffset"; writer.WriteValue(typeName); } // 处理数组与集合 else if (type.IsArray || typeof(IEnumerable).IsAssignableFrom(type) && type != typeof(string)) { writer.WriteStartArray(); foreach (var item in (IEnumerable)value) { WriteJson(writer, item, serializer); } writer.WriteEndArray(); } // 处理嵌套对象 else { writer.WriteStartObject(); foreach (var prop in type.GetProperties(BindingFlags.Public | BindingFlags.Instance)) { var propValue = prop.GetValue(value); writer.WritePropertyName(prop.Name); WriteJson(writer, propValue, serializer); } writer.WriteEndObject(); } } public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) { throw new NotImplementedException("此转换器仅支持序列化操作"); } }
使用示例:
var data = new { organisationId = Guid.Empty, submittedAt = new DateTimeOffset(2023, 5, 8, 21, 58, 50, TimeSpan.Zero), multiple = new int[] { 123 }, obj = new { innerEx1 = "hello world", innerEx2 = 123 } }; string resultJson = JsonConvert.SerializeObject(data, Formatting.Indented, new TypeNameJsonConverter());
场景2:从现有JSON字符串转换
先解析JSON为JToken,遍历所有节点并替换值为对应类型名称:
using Newtonsoft.Json; using Newtonsoft.Json.Linq; using System; public static class JsonTypeMapper { public static JToken ReplaceValuesWithTypeNames(JToken token) { switch (token.Type) { case JTokenType.Object: foreach (JProperty prop in token.Children<JProperty>()) { prop.Value = ReplaceValuesWithTypeNames(prop.Value); } return token; case JTokenType.Array: for (int i = 0; i < token.Count(); i++) { token[i] = ReplaceValuesWithTypeNames(token[i]); } return token; case JTokenType.String: string strVal = token.Value<string>(); if (Guid.TryParse(strVal, out _)) return JToken.FromObject("Guid"); else if (DateTimeOffset.TryParse(strVal, out _)) return JToken.FromObject("DateTimeOffset"); else return JToken.FromObject("string"); case JTokenType.Integer: return JToken.FromObject("int"); case JTokenType.Float: return JToken.FromObject("double"); case JTokenType.Boolean: return JToken.FromObject("bool"); case JTokenType.Null: return JToken.FromObject("null"); default: return JToken.FromObject(token.Type.ToString()); } } }
使用示例:
string inputJson = @" { ""organisationId"": ""00000000-0000-0000-0000-000000000000"", ""submittedAt"": ""2023-05-08T21:58:50+00:00"", ""multiple"": [ 123 ], ""obj"": { ""innerEx1"": ""hello world"", ""innerEx2"": 123 } }"; JToken inputToken = JToken.Parse(inputJson); JToken outputToken = JsonTypeMapper.ReplaceValuesWithTypeNames(inputToken); string resultJson = outputToken.ToString(Formatting.Indented);
内容的提问来源于stack exchange,提问作者James Coyle
相关产品推荐
相关产品推荐

