问题
Is it possible to create an attribute to serialize some subelements inline (Formatting.None) with newtonsoft json.net?
I have a very huge set of data and I want to keep it readeable. Some subelements are not very important and can be writen inline.
{
"name": "xxx",
"desc": "xxx",
"subelem": [
{"val1": 1, "val2": 2, ...}, //inline,
{"val1": 1, "val2": 2, ...},
...
]
"subelem2": {
"val1": 1,
"val2": 2,
...
}
}
I want to force the inline serialization for some sub objects of my models. In this case, "subelem" items will be written inline. Thanks
回答1:
Adding the converter as a JsonConverterAttribute on a class is trickier because the simplest implementation will lead to an infinite recursion as the converter calls itself. Thus it's necessary to disable the converter for recursive calls in a thread-safe manner, like so:
public class NoFormattingConverter : JsonConverter
{
[ThreadStatic]
static bool cannotWrite;
// Disables the converter in a thread-safe manner.
bool CannotWrite { get { return cannotWrite; } set { cannotWrite = value; } }
public override bool CanWrite { get { return !CannotWrite; } }
public override bool CanRead { get { return false; } }
public override bool CanConvert(Type objectType)
{
throw new NotImplementedException(); // Should be applied as a property rather than included in the JsonSerializerSettings.Converters list.
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
throw new NotImplementedException();
}
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
using (new PushValue<bool>(true, () => CannotWrite, val => CannotWrite = val))
using (new PushValue<Formatting>(Formatting.None, () => writer.Formatting, val => writer.Formatting = val))
{
serializer.Serialize(writer, value);
}
}
}
public struct PushValue<T> : IDisposable
{
Action<T> setValue;
T oldValue;
public PushValue(T value, Func<T> getValue, Action<T> setValue)
{
if (getValue == null || setValue == null)
throw new ArgumentNullException();
this.setValue = setValue;
this.oldValue = getValue();
setValue(value);
}
#region IDisposable Members
// By using a disposable struct we avoid the overhead of allocating and freeing an instance of a finalizable class.
public void Dispose()
{
if (setValue != null)
setValue(oldValue);
}
#endregion
}
And then apply it to a class (or property) like so:
[JsonConverter(typeof(NoFormattingConverter))]
public class NestedClass
{
public string[] Values { get; set; }
}
public class TestClass
{
public string AValue { get; set; }
public NestedClass NestedClass { get; set; }
public string ZValue { get; set; }
public static void Test()
{
var test = new TestClass { AValue = "A Value", NestedClass = new NestedClass { Values = new[] { "one", "two", "three" } }, ZValue = "Z Value" };
Debug.WriteLine(JsonConvert.SerializeObject(test, Formatting.Indented));
}
}
The output of the Test()
method above is:
{ "AValue": "A Value", "NestedClass":{"Values":["one","two","three"]}, "ZValue": "Z Value" }
来源:https://stackoverflow.com/questions/30831895/newtonsoft-inline-formatting-for-subelement-while-serializing