基于属性数量的自定义 JSON 格式

Posted

技术标签:

【中文标题】基于属性数量的自定义 JSON 格式【英文标题】:Custom JSON formatting based on number of properties 【发布时间】:2020-02-06 05:13:36 【问题描述】:

我正在寻找一种在序列化后将 JSON 字符串拆分为多行的方法,在每个 Nth 属性之后插入一个换行符。

例如,我有这个类:

public class Obj

    public string Property1  get; set; 
    public string Property2  get; set; 
    public string[] Property3  get; set; 
    public string Property4  get; set; 
    public string Property5  get; set; 
    public string Property6  get; set; 
    public TimeSpan Time  get; set; 
    public string Property7  get; set; 
    public string Property8  get; set; 
    public string Property9  get; set; 
    public string Property10  get; set; 
    public string[] Property11  get; set; 

初始化为这样的:

var root = new Obj
    
        Property1 = "value1",
        Property2 = "value2",
        Property3 = new[] "test", "test1", "test3",
        Property4 = "value4",
        Property5 = "value5",
        Property6 = "value6",
        Time = TimeSpan.FromSeconds(13),
        Property7 = "value7",
        Property8 = "value8",
        Property9 = "value9",
        Property10 = "value10",
        Property11 = new string[] "asdf", "basdf"
    ;

当我调用JsonConvert.SerializeObject(root) 时,它会打印出来:

"Property1":"value1","Property2":"value2","Property3":["test","test1","test3"],"Property4":"value4","Property5":"value5","Property6":"value6","Time":"00:00:13","Property7":"value7","Property8":"value8","Property9":"value9","Property10":"value10","Property11":["asdf","basdf"]

我想在每个第 N 个属性之后添加一个新行。假设每 3 个属性。

我尝试过这样的事情:

public static string ReplaceEveryNth(string fullString, string pattern, int n)

    if (n < 1)  return fullString; 

    var index = 1;
    return Regex.Replace(fullString, pattern, m =>
    
        return (index++ % n == 0) ? m.Value + Environment.NewLine : m.Value;
    );

并这样称呼它来匹配 JSON 字符串上的键值对:

var replaced = ReplaceEveryNth(json, "(\".*?\":\".*?\"),", 3);

现在,这适用于简单的属性。但是当我开始引入像数组这样的类型时,Regex 变得更加复杂。

我想知道是否有更简单的方法。

【问题讨论】:

您是否只需要每个 x 属性的新行?或者你会接受完全缩进的 JSON 输出吗?您可以将第二个参数传递给 SerializeObject 方法以指示您想要格式化输出。 newtonsoft.com/json/help/html/… 正如问题所述,我想根据计数缩进 【参考方案1】:

我不确定这是否正是您要寻找的,但您可以尝试实现自定义 JsonWriter 以在每个第 N 个属性名称之前插入一个换行符(假设您使用的是 Json.Net):

public class CustomJsonWriter : JsonTextWriter

    public int N  get; set; 
    private int propertyCount = 0;

    public CustomJsonWriter(TextWriter textWriter, int n) : base(textWriter)
    
        N = n;
    

    public override void WritePropertyName(string name, bool escape)
    
        if (propertyCount > 0 && propertyCount % N == 0)
            WriteWhitespace(Environment.NewLine);

        base.WritePropertyName(name, escape);
        propertyCount++;
    

辅助方法将使其易于使用:

public static string SerializeWithCustomFormatting(object obj, int n)

    using (TextWriter sw = new StringWriter())
    using (JsonWriter writer = new CustomJsonWriter(sw, n))
    
        JsonSerializer ser = new JsonSerializer();
        ser.Serialize(writer, obj);
        return sw.ToString();
    

那么你可以这样做:

string json = SerializeWithCustomFormatting(root, 3);

使用您的示例,它会产生如下输出:

"Property1":"value1","Property2":"value2","Property3":["test","test1","test3"]
,"Property4":"value4","Property5":"value5","Property6":"value6"
,"Time":"00:00:13","Property7":"value7","Property8":"value8"
,"Property9":"value9","Property10":"value10","Property11":["asdf","basdf"]

小提琴:https://dotnetfiddle.net/gG8az2

【讨论】:

这是完美的。 WritePropertyName 是我要找的那个。 很高兴我能帮上忙。【参考方案2】:

完全缩进的 json 对你有用吗?

 var json = JsonConvert.SerializeObject(o, new JsonSerializerSettings
            
                Formatting = Formatting.Indented
            );

更新

由于完全缩进的 json 不够好,您可以尝试自定义转换器。

结果

"Property1":"value1","Property2":"value2","Property3":["test","test1","test3"]
,"Property4":"value4","Property5":"value5","Property6":"value6"
,"Time":"00:00:13","Property7":"value7","Property8":"value8"
,"Property9":"value9","Property10":"value10","Property11":["asdf","basdf"]

转换器

public class CustomLineBreakerConverter : JsonConverter

  private readonly uint n;
  private uint i = 1;

  public CustomLineBreakerConverter(uint n)  this.n = n; 

  public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
  
    // Scaffolding from https://www.newtonsoft.com/json/help/html/CustomJsonConverter.htm

    // Please note this will not work recursively (only the top level will be have the new lines
    JToken t = JToken.FromObject(value);

    if (t.Type != JTokenType.Object)
    
      t.WriteTo(writer);
    
    else
    
      JObject o = (JObject)t;
      var properties = o.Properties();
      writer.WriteStartObject();
      foreach( var p in properties)
      
        p.WriteTo(writer);
        if (i++ % n == 0)
        
          writer.WriteWhitespace("\r\n");  // This will write a new line after the property even if no more properties
        
      
      writer.WriteEndObject();
    
  
  public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
     => throw new NotImplementedException("This converter is meant only for writing");
  public override bool CanConvert(Type objectType) => true;

测试代码

var o = new

    Property1 = "value1",
    Property2 = "value2",
    Property3 = new[]  "test", "test1", "test3" ,
    Property4 = "value4",
    Property5 = "value5",
    Property6 = "value6",
    Time = TimeSpan.FromSeconds(13),
    Property7 = "value7",
    Property8 = "value8",
    Property9 = "value9",
    Property10 = "value10",
    Property11 = new string[]  "asdf", "basdf" 
;

var json = JsonConvert.SerializeObject(o, new JsonSerializerSettings

    Formatting = Formatting.None,
    Converters = new List<JsonConverter>()  new CustomLineBreakerConverter(3) 
);

Console.WriteLine(json);

【讨论】:

我想在每个属性后缩进一次,而不是每个属性。

以上是关于基于属性数量的自定义 JSON 格式的主要内容,如果未能解决你的问题,请参考以下文章

用于深层嵌套对象的自定义Json Serializer

Grails JSON 编组器中的自定义字符串格式

System.Json - 属性序列化跳过的自定义规则

来自熊猫数据框的自定义 JSON 格式输出

如何读取多种格式的自定义属性值?

来自 MYSQL PDO 的自定义格式 JSON,用于 NVD3.js