【问题标题】:Custom JSON formatting based on number of properties基于属性数量的自定义 JSON 格式
【发布时间】: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/…
  • 正如问题所述,我想根据计数缩进

标签: c# json regex


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

【讨论】:

  • 我想在每个属性后缩进一次,而不是每个属性。
猜你喜欢
  • 2020-01-22
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多