我已经仔细研究了文档,StackOverflow等,似乎找不到这个...
我想做的是将对象的简单值类型序列化/反序列化为值,而不是对象,如下所示:
public class IPAddress
{
byte[] bytes;
public override string ToString() {... etc.
}
public class SomeOuterObject
{
string stringValue;
IPAddress ipValue;
}
IPAddress ip = new IPAddress("192.168.1.2");
var obj = new SomeOuterObject() {stringValue = "Some String", ipValue = ip};
string json = JsonConverter.SerializeObject(obj);
我想要的是让 json 像这样序列化:
// json = {"someString":"Some String","ipValue":"192.168.1.2"} <- value serialized as value, not subobject
不是 ip 成为嵌套对象的地方,例如:
// json = {"someString":"Some String","ipValue":{"value":"192.168.1.2"}}
有谁知道如何做到这一点?谢谢!(附言我正在一个大型的遗留 .NET 代码库上固定 Json 序列化,所以我无法真正更改任何现有类型,但我可以增强/分解/装饰它们以促进 Json 序列化。
IPAddress
类的自定义JsonConverter
来处理此问题。 以下是您需要的代码:
public class IPAddressConverter : JsonConverter
{
public override bool CanConvert(Type objectType)
{
return (objectType == typeof(IPAddress));
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
return new IPAddress(JToken.Load(reader).ToString());
}
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
JToken.FromObject(value.ToString()).WriteTo(writer);
}
}
然后,将 [JsonConverter]
属性添加到IPAddress
类中,即可开始:
[JsonConverter(typeof(IPAddressConverter))]
public class IPAddress
{
byte[] bytes;
public IPAddress(string address)
{
bytes = address.Split('.').Select(s => byte.Parse(s)).ToArray();
}
public override string ToString()
{
return string.Join(".", bytes.Select(b => b.ToString()).ToArray());
}
}
这是一个工作演示:
class Program
{
static void Main(string[] args)
{
IPAddress ip = new IPAddress("192.168.1.2");
var obj = new SomeOuterObject() { stringValue = "Some String", ipValue = ip };
string json = JsonConvert.SerializeObject(obj);
Console.WriteLine(json);
}
}
public class SomeOuterObject
{
public string stringValue { get; set; }
public IPAddress ipValue { get; set; }
}
输出:
{"stringValue":"Some String","ipValue":"192.168.1.2"}
这是 Customise NewtonSoft.Json for Value Object 序列化的答案,涉及 DDD 中的值对象。但是这个问题被标记为与这个问题重复,我认为这并不完全正确。
我借用了 ValueObjectConverter 的代码 https://github.com/eventflow/EventFlow,我只做了一些小的更改。
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using FluentAssertions;
using Newtonsoft.Json;
using Xunit;
namespace Serialization
{
public class ValueObjectSerializationTests
{
class SomeClass
{
public IPAddress IPAddress { get; set; }
}
[Fact]
public void FactMethodName()
{
var given = new SomeClass
{
IPAddress = new IPAddress("192.168.1.2")
};
var jsonSerializerSettings = new JsonSerializerSettings()
{
Converters = new List<JsonConverter>
{
new ValueObjectConverter()
}
};
var json = JsonConvert.SerializeObject(given, jsonSerializerSettings);
var result = JsonConvert.DeserializeObject<SomeClass>(json, jsonSerializerSettings);
var expected = new SomeClass
{
IPAddress = new IPAddress("192.168.1.2")
};
json.Should().Be("{"IPAddress":"192.168.1.2"}");
expected.ShouldBeEquivalentTo(result);
}
}
public class IPAddress:IValueObject
{
public IPAddress(string value)
{
Value = value;
}
public object GetValue()
{
return Value;
}
public string Value { get; private set; }
}
public interface IValueObject
{
object GetValue();
}
public class ValueObjectConverter : JsonConverter
{
private static readonly ConcurrentDictionary<Type, Type> ConstructorArgumenTypes = new ConcurrentDictionary<Type, Type>();
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
if (!(value is IValueObject valueObject))
{
return;
}
serializer.Serialize(writer, valueObject.GetValue());
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
var parameterType = ConstructorArgumenTypes.GetOrAdd(
objectType,
t =>
{
var constructorInfo = objectType.GetConstructors(BindingFlags.Public | BindingFlags.Instance).First();
var parameterInfo = constructorInfo.GetParameters().Single();
return parameterInfo.ParameterType;
});
var value = serializer.Deserialize(reader, parameterType);
return Activator.CreateInstance(objectType, new[] { value });
}
public override bool CanConvert(Type objectType)
{
return typeof(IValueObject).IsAssignableFrom(objectType);
}
}
}
public class IPAddress
{
byte[] bytes;
public override string ToString() {... etc.
}
IPAddress ip = new IPAddress("192.168.1.2");
var obj = new () {ipValue = ip.ToString()};
string json = JsonConverter.SerializeObject(obj);
您正在序列化整个 IP 地址实例。也许只是尝试将地址序列化为字符串。(这假定您已经实现了 ToString 方法。
这是一个用于简单值对象的通用转换类,我计划将其包含在 Activout.RestClient 的下一次更新中。一个"简单值对象",作为具有以下对象的对象:
- 无默认构造函数
- 名为"值"的公共属性
- 采用与 Value 属性相同的类型的构造函数
var settings = new JsonSerializerSettings
{
Converters = new List<JsonConverter> {new SimpleValueObjectConverter()}
};
<小时 /> public class SimpleValueObjectConverter : JsonConverter
{
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
var valueProperty = GetValueProperty(value.GetType());
serializer.Serialize(writer, valueProperty.GetValue(value));
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue,
JsonSerializer serializer)
{
var valueProperty = GetValueProperty(objectType);
var value = serializer.Deserialize(reader, valueProperty.PropertyType);
return Activator.CreateInstance(objectType, value);
}
public override bool CanConvert(Type objectType)
{
if (GetDefaultConstructor(objectType) != null) return false;
var valueProperty = GetValueProperty(objectType);
if (valueProperty == null) return false;
var constructor = GetValueConstructor(objectType, valueProperty);
return constructor != null;
}
private static ConstructorInfo GetValueConstructor(Type objectType, PropertyInfo valueProperty)
{
return objectType.GetConstructor(new[] {valueProperty.PropertyType});
}
private static PropertyInfo GetValueProperty(Type objectType)
{
return objectType.GetProperty("Value");
}
private static ConstructorInfo GetDefaultConstructor(Type objectType)
{
return objectType.GetConstructor(new Type[0]);
}
}
有几种不同的方法可以解决这个问题,具体取决于您能够花费的工作量和对现有类更改的容忍度。
一种方法是将类定义为 DataContract,并将类中的元素显式标识为 DataMembers。Netwonsoft在其序列化中识别并使用这些属性。此方法的优点是,现在可以使用使用数据协定序列化的其他方法序列化类。
[DataContract]
public class IPAddress
{
private byte[] bytes;
// Added this readonly property to allow serialization
[DataMember(Name = "ipValue")]
public string Value
{
get
{
return this.ToString();
}
}
public override string ToString()
{
return "192.168.1.2";
}
}
这是我用来序列化的代码(我可能使用的是旧版本,因为我没有看到 SerializeObject 方法(:
IPAddress ip = new IPAddress();
using (StringWriter oStringWriter = new StringWriter())
{
using (JsonTextWriter oJsonWriter = new JsonTextWriter(oStringWriter))
{
JsonSerializer oSerializer = null;
JsonSerializerSettings oOptions = new JsonSerializerSettings();
// Generate the json without quotes around the name objects
oJsonWriter.QuoteName = false;
// This can be used in order to view the rendered properties "nicely"
oJsonWriter.Formatting = Formatting.Indented;
oOptions.NullValueHandling = NullValueHandling.Ignore;
oSerializer = JsonSerializer.Create(oOptions);
oSerializer.Serialize(oJsonWriter, ip);
Console.WriteLine(oStringWriter.ToString());
}
}
这是输出:
{
ipValue: "192.168.1.2"
}
另一种方法是创建自己的 JsonConverter 继承器,它可以准确序列化您需要的内容,而无需修改类的内部结构:
public class JsonToStringConverter : JsonConverter
{
public override bool CanConvert(Type objectType)
{
return true;
}
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)
{
writer.WriteStartObject();
writer.WritePropertyName(value.GetType().Name);
writer.WriteValue(Convert.ToString(value));
writer.WriteEndObject();
}
}
此类仅将类的 tostring 值与类名一起写入。更改名称可以通过类上的其他属性来完成,我没有展示。
然后,该类将如下所示:
[JsonConverter(typeof(JsonToStringConverter))]
public class IPAddress
{
private byte[] bytes;
public override string ToString()
{
return "192.168.1.2";
}
}
输出为:
{
IPAddress: "192.168.1.2"
}
使用 Cinchoo ETL - 一个用于解析/写入 JSON 文件的开源库,您可以通过 ValueConverter 或回调机制控制每个对象成员的序列化。
方法一:
下面的示例演示如何使用成员级值转换器序列化"某个外部对象">
public class SomeOuterObject
{
public string stringValue { get; set; }
[ChoTypeConverter(typeof(ToTextConverter))]
public IPAddress ipValue { get; set; }
}
价值转换器是
public class ToTextConverter : IChoValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
return value;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
return value.ToString();
}
}
最后将对象序列化为文件
using (var jr = new ChoJSONWriter<SomeOuterObject>("ipaddr.json")
)
{
var x1 = new SomeOuterObject { stringValue = "X1", ipValue = IPAddress.Parse("12.23.21.23") };
jr.Write(x1);
}
输出是
[
{
"stringValue": "X1",
"ipValue": "12.23.21.23"
}
]
方法2:
这是将值转换器回调挂接到"ipValue"属性的替代方法。这种方法很精简,无需仅为此操作创建价值转换器。
using (var jr = new ChoJSONWriter<SomeOuterObject>("ipaddr.json")
.WithField("stringValue")
.WithField("ipValue", valueConverter: (o) => o.ToString())
)
{
var x1 = new SomeOuterObject { stringValue = "X1", ipValue = IPAddress.Parse("12.23.21.23") };
jr.Write(x1);
}
希望这有帮助。
免责声明:我是图书馆的作者。