Skip to content

Instantly share code, notes, and snippets.

@fliedonion
Last active December 20, 2022 10:59
Show Gist options
  • Save fliedonion/6a0dc56220813473181f7f19b4efc1c4 to your computer and use it in GitHub Desktop.
Save fliedonion/6a0dc56220813473181f7f19b4efc1c4 to your computer and use it in GitHub Desktop.
JSON.NET Deserialize null string to string.Empty.
using JsonConvertOptionForm.Entity;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Diagnostics;
using System.Drawing;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace JsonConvertOptionForm
{
public partial class SampleCodeForm : Form
{
public SampleCodeForm()
{
InitializeComponent();
}
// Add two buttons in the designer.
private void writeTestJsonButton_Click(object sender, EventArgs e)
{
var x = new SomeEntity
{
Number = 10,
// Text = "TextProp" + new string('X', 1024 * 1024 * 30), // 30MB // 30MBでもexistingValue 引数はセットされなかった。
// The existingValue argument was not set even at 30 MB.
Text = "TextProp",
TextNull = null,
Products = new List<string> { "l1", "l2", null },
Dic = new Dictionary<string, int> { { "abc", 100 }, { "efg", 100 } },
Nested = new List<List<string>>{
new List<string> { "1-1", "1-2" },
new List<string> { "2-1", null, "2-3" },
},
UpdateDate = DateTime.Now,
UpdateDates = new List<DateTime> { DateTime.Now, DateTime.Now, }
};
var json = Newtonsoft.Json.JsonConvert.SerializeObject(x, Newtonsoft.Json.Formatting.Indented);
File.WriteAllText("sample.json", json, new UTF8Encoding(false));
}
private void readJsonTestButton_Click(object sender, EventArgs e)
{
var json =File.ReadAllText("sample.json", new UTF8Encoding(false));
{
var defaultDeserialize = Newtonsoft.Json.JsonConvert.DeserializeObject<SomeEntity>(json);
Debug.Print("-- with default converter ------------------------");
Debug.Print(defaultDeserialize.Text.Length.ToString());
Debug.Print(defaultDeserialize.TextNull ?? "<null>");
Debug.Print(defaultDeserialize.Number.ToString() + " " + defaultDeserialize.Number.GetType());
Debug.Print(defaultDeserialize.Products[2] ?? "<null>");
Debug.Print(defaultDeserialize.Nested[1][1] ?? "<null>");
Debug.Print($"'{defaultDeserialize.Products[2] ?? "<null>"}' is null {defaultDeserialize.Products[2] == null} : JSON上のnullアイテム が null かどうか。expect true");
Debug.Print($"'{defaultDeserialize.Nested[1][1] ?? "<null>"}' is null {defaultDeserialize.Nested[1][1] == null} : JSON上のnullアイテム が null かどうか。expect true");
Debug.Print($"'{defaultDeserialize.Products[1]}' is NOT null {defaultDeserialize.Products[1] != null} : JSON上の非nullアイテムもちゃんと取れていることの確認");
Debug.Print($"'{defaultDeserialize.Nested[1][2]}' is NOT null {defaultDeserialize.Nested[1][2] != null} : JSON上の非nullアイテムもちゃんと取れていることの確認");
}
{
var desNumText = JsonConvert.DeserializeObject<SomeEntityWithNumberAsText>(json, new NullToEmptyForStringTypeDeserializeConverter());
Debug.Print("-- with my StringConverter ------------------------");
Debug.Print(desNumText.Text.Length.ToString());
Debug.Print(desNumText.TextNull ?? "<null>");
Debug.Print((desNumText.Number ?? "<null>") + " " + desNumText.Number.GetType() + ": シリアル化(JSON)時点では数値オブジェクト、デシリアライズで文字列プロパティで受け付けたとき。");
Debug.Print(desNumText.Products[2] ?? "<null>");
Debug.Print(desNumText.Nested[1][1] ?? "<null>");
Debug.Print($"'{desNumText.Products[2] ?? "<null>"}' is null {desNumText.Products[2] == null} : JSON上のnullアイテム が null かどうか。expect false");
Debug.Print($"'{desNumText.Nested[1][1] ?? "<null>"}' is null {desNumText.Nested[1][1] == null} : JSON上のnullアイテム が null かどうか。expect false");
Debug.Print($"'{desNumText.Products[1]}' is not null {desNumText.Products[1] != null} : JSON上の非nullアイテムもちゃんと取れていることの確認");
Debug.Print($"'{desNumText.Nested[1][2]}' is not null {desNumText.Nested[1][2] != null} : JSON上の非nullアイテムもちゃんと取れていることの確認");
Debug.Print("finish desealize");
}
// if you want to use settings. you can specify Converter like following.
var settings = new JsonSerializerSettings
{
Converters = new List<JsonConverter> { new NullToEmptyForStringTypeDeserializeConverter(), new NullToEmptyForStringTypeDeserializeConverter() }
};
}
public class NullToEmptyForStringTypeDeserializeConverter : JsonConverter<String>
{
// when CanWrite == false, use this only for deserialize.
public override bool CanWrite => false;
public override void WriteJson(JsonWriter writer, String value, JsonSerializer serializer)
{
throw new NotImplementedException("Not Support for Serialize");
}
public override String ReadJson(JsonReader reader, Type objectType, String existingValue, bool hasExistingValue, JsonSerializer serializer)
{
if (hasExistingValue) throw new JsonSerializationException($"{nameof(NullToEmptyForStringTypeDeserializeConverter)} does not support desealize with existingValue.");
if (reader.Value != null && reader.ValueType != typeof(String)) {
// TODO: さらに複雑には別のConverterを呼んだ方が良いのかもしれないが、未検証。(間違えるとConverter同士が呼びあい無限ループの可能性もあるのでハンドル済みCOnverterが持てるオーバーロード作るとか工夫しないとだめかも)
// TODO: For more complexity, it may be better to call another Converter, but this has not been tested. (If you make a mistake, there is a possibility that converters will call each other, resulting in an infinite loop, so you may need to create an overload that allows handled COnverters to have their own handles.)
//
// if (serializer.Converters.Count > 0)
// {
// foreach (var conv in serializer.Converters)
// {
// if (!ReferenceEquals(this, conv) && conv.CanRead && conv.CanConvert(objectType))
// {
// return conv.ReadJson(reader, objectType, existingValue, serializer) as string;
// }
// }
// }
// 他の型である場合は、JObjectに任せる。
var token = JObject.ReadFrom(reader);
return token.ToObject<String>();
}
string s = (string)reader.Value ?? "";
return s;
}
}
}
}
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace JsonConvertOptionForm.Entity
{
public class SomeEntity
{
public int Number { get; set; }
public string Text { get; set; }
public string TextNull { get; set; }
public List<string> Products { get; set; }
public Dictionary<string, int> Dic { get; set; }
public List<List<string>> Nested { get; set; }
public DateTime UpdateDate;
public List<DateTime> UpdateDates;
}
public class SomeEntityWithNumberAsText
{
public string Number { get; set; }
public string Text { get; set; }
public string TextNull { get; set; }
public List<string> Products { get; set; }
public Dictionary<string, int> Dic { get; set; }
public List<List<string>> Nested { get; set; }
public DateTime UpdateDate;
public List<DateTime> UpdateDates;
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment