C# Json.net如何将对象序列化为值

C# Json.net如何将对象序列化为值,c#,json.net,C#,Json.net,我仔细阅读了文档、StackOverflow等,似乎找不到这个 我要做的是将对象的简单值类型序列化/反序列化为值,而不是对象,如下所示: public class IPAddress { byte[] bytes; public override string ToString() {... etc. } public class SomeOuterObject { string stringValue; IPAddress ipValue; } IPAdd

我仔细阅读了文档、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
有人知道怎么做吗?谢谢(顺便说一句,我将Json序列化放在一个庞大的遗留.NET代码库上,因此我无法真正更改任何现有类型,但我可以对它们进行扩充/因子/修饰以促进Json序列化。)


您正在序列化整个IP地址实例。可能只是尝试将地址序列化为字符串。(这假定您已经实现了ToString方法。)

根据您所能付出的努力程度和对现有类更改的容忍度,有几种不同的方法来实现这一点

一种方法是将类定义为,并将类中的元素显式标识为。Netwonsoft在其序列化中识别并使用这些属性。这种方法的优点是,现在可以使用其他使用datacontract序列化的方法对类进行序列化

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);
    [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方法,我可能使用的是旧版本):

以下是输出:

{
  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"
}

您可以使用
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"}
这是对DDD中的值对象的回答。但这个问题被标记为与这个问题重复,我认为这不是完全正确的

我从中借用了ValueObjectConverter的代码 ,我只做了一些小改动

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);
        }
    }
}
使用系统;
使用System.Collections.Concurrent;
使用System.Collections.Generic;
使用System.Linq;
运用系统反思;
使用FluentAssertions;
使用Newtonsoft.Json;
使用Xunit;
命名空间序列化
{
公共类ValueObjectSerializationTests
{
上课
{
公共IPAddress IPAddress{get;set;}
}
[事实]
public void FactMethodName()
{
var给定=新的SomeClass
{
IP地址=新的IP地址(“192.168.1.2”)
};
var jsonSerializerSettings=new jsonSerializerSettings()
{
转换器=新列表
{
新的ValueObjectConverter()
}
};
var json=JsonConvert.SerializeObject(给定jsonSerializerSettings);
var result=JsonConvert.DeserializeObject(json,jsonSerializerSettings);
var expected=new SomeClass
{
IP地址=新的IP地址(“192.168.1.2”)
};
json.Should().Be(“{\”IPAddress\“:\”192.168.1.2\”);
预期。应等同于(结果);
}
}
公共类IP地址:IValueObject
{
公共IP地址(字符串值)
{
价值=价值;
}
公共对象GetValue()
{
返回值;
}
公共字符串值{get;private set;}
}
公共接口对象
{
对象GetValue();
}
公共类ValueObjectConverter:JsonConverter
{
私有静态只读ConcurrentDictionary构造函数argumEntypes=new ConcurrentDictionary();
公共重写void WriteJson(JsonWriter编写器、对象值、JsonSerializer序列化器)
{
如果(!(值为IValueObject valueObject))
{
返回;
}
serializer.Serialize(writer,valueObject.GetValue());
}
公共重写对象ReadJson(JsonReader阅读器,类型objectType,对象existingValue,JsonSerializer序列化程序)
{
var parameterType=ConstructorArgumenTypes.GetOrAdd(
对象类型,
t=>
{
var constructorInfo=objectType.GetConstructors(BindingFlags.Public | BindingFlags.Instance).First();
var parameterInfo=constructorInfo.GetParameters().Single();
返回parameterInfo.ParameterType;
});
var value=serializer.Deserialize(reader,parameterType);
返回Activator.CreateInstance(objectType,new[]{value});
}
公共覆盖布尔CanConvert(类型objectType)
{
返回类型(IValueObject).IsAssignableFrom(objectType);
}
}
}
使用-一个用于解析/写入JSON文件的开源库,您可以通过ValueConverter或回调机制控制每个对象成员的序列化

方法1:

下面的示例显示了如何使用成员级别的ValueConverter序列化“SomeOuterObject”

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);
}
方法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);
}
使用(var jr=new ChoJSONWriter(“ipaddr.json”)
.WithField(“stringValue”)
.WithField(“ipValue”,值转换器:(o)=>o.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"
 }
]
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);
}