【问题标题】:Serialize/Deserialize Object with each field type preserved of an object序列化/反序列化对象,并保留对象的每个字段类型
【发布时间】:2019-01-01 05:36:39
【问题描述】:

我正在编写一个实用程序,它读取文本文件 (json) 并动态构造一个 json 对象并将其发送到 REST API。为此,我希望我的序列化 json 对象(我使用 C# 中的 Json.Net 创建)还保留每个字段的类型信息及其值。

例如,我希望我的 json 文件采用类似的格式:

{
    "fieldString": {
        "type": "string",
        "value": "my custom string data"
    },
    "fieldTimeStamp": {
        "type": "date",
        "value": "2018-12-10T08:25:55.150Z"
    },
    "fieldNumber": {
        "type": "number",
        "value": 999999.999
    },
    "fieldGeopoint": {
        "type": "geopoint",
        "value": {
            "_latitude": 0.0,
            "_longitude": 0.0
        }
    },
}

感谢社区成员提出的问题,这里是附加信息,我希望这会有所帮助...

我的实用程序将是数据上传实用程序,它将独立于任何应用程序读取数据。这将从 json 文件中读取数据并构造一个正确的对象来调用 REST API(任何)。例如,GeoPoint 对象,它不是原始数据类型,不同的语言可能有不同的名称,与此对象关联的属性。

创建 json 文件的工具将负责提供类型信息以及每个对象的字段值。我的数据上传实用程序将决定如何解释 GeoPoint 以将其传递给其他 API,例如,在 Azure CosmosDB 中,geo-point 被称为“Point”,而在 Good world 中,它被称为“GeoPoint”,其他人可能有不同相同基础信息的名称。或者例如,有些可能会区分“int”和“float”,而有些可能不会。

【问题讨论】:

  • 为什么fieldArray 实际上不是一个数组.....这里还有一个问题吗?
  • 那么...是什么阻止了你? (除了这可能是一个坏主意 - json 应该包含数据,类型属于 json 模式)
  • 谢谢 Erik,这是我的错,数组将是默认的 Json 数组类型。
  • @ZoharPeled 我的实用程序将是数据上传实用程序,它将独立于任何应用程序读取工作。这将从 json 文件中读取数据并构造一个正确的对象来调用 REST API(任何)。例如,GeoPoint 对象,它不是原始数据类型,不同的语言可能有不同的名称,与此对象关联的属性。
  • 你从哪里得到类型名称?未知语言如何知道如何使用 geopointnumber 类型的数据?另外,如果我们沿着这条路走——地理点有纬度和经度,但它们没有类型......你将不得不递归地为复杂的对象执行此操作,反序列化将是地狱。

标签: c# json.net


【解决方案1】:

这已经是 JSON.Net 序列化程序中内置的一项功能:

How to serialize object to json with type info using Newtonsoft.Json?

我相信您可以利用这一点:区别实际上在于原始类型和对象类型。如果您有一个对象属性,它将被支持,如果它是一个原始类型(可能不是)。因此,您需要为每个原始类型创建一个单独的类。您将创建自己的 String 对象、Date 对象、Number 对象等(在 C# 端)。

另外,您可以直接使用 JsonWriter/JsonReader。它们不太难使用,并且可以让您对您的协议进行最佳控制。直接代码将使其易于调试,并且还具有执行非常好的副作用。 (通常,JSON.net 使用反射和/或运行时编译的辅助类)

【讨论】:

  • 这应该是一个势均力敌的投票。您应该知道,拥有超过 10k 的声望点。
  • 我从来没有读过这些规则,我对它们也不感兴趣。我只是一个提出问题并回答问题的编码员。这些规则通常不利于编码人员、社区和文化。
  • 你好@Todd,谢谢,我已经浏览了这篇文章,但我的问题有点不同,我正在寻找的是类对象属性层的类型信息,但是在这我得到的是对象级别的类型信息。直到我错过任何其他设置。
  • @Ravi 哦,我明白了,是的,这将是正常的治疗。我会更新我的答案。
【解决方案2】:

虽然我按照@ZoharPeled 的建议使用 Json Schema 路径。

但正如@Todd 所建议的那样,我创建了以下解决方案,该解决方案在序列化对象时保留字段的对象类型信息。我把它放在这里只是为了参考,以防万一有人想参考它。

    class TypeInfoConverter : JsonConverter
    {
        public override bool CanConvert(Type objectType)
        {
            return true;
        }

        public override bool CanRead
        {
            get { return false; }
        }

        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)
        {
            var converters = serializer.Converters.Where(x => !(x is TypeInfoConverter)).ToArray();
            JToken jToken = JToken.FromObject(value);
            JObject jObject;
            switch (jToken.Type)
            {
                case JTokenType.Object:
                case JTokenType.Array:
                case JTokenType.Bytes:
                case JTokenType.Date:
                    jObject = JObject.FromObject(new Converter(value));
                    jObject.WriteTo(writer, converters);
                    break;
                default:
                    //jObject = JObject.FromObject(new Converter(value));
                    //jObject.WriteTo(writer);
                    jToken.WriteTo(writer);
                    break;
            }
        }

        class Converter
        {
            public Dictionary<string, object> _attr = new Dictionary<string, object>();
            public object value;

            public Converter(object value)
            {
                this.value = value;
                addAttributes();
            }

            private void addAttributes()
            {
                Type t = value.GetType();
                _attr["type"] = t.Name;

                if (t.IsGenericType
                    && (value is IList || value is IDictionary))
                {
                    collectionAttributes(value, _attr, t);
                }
                else if (t.IsEnum)
                {
                    _attr["type"] = "enum";
                    _attr["class"] = t.Name;
                    //attributes["meaning"] = value.ToString();
                }
            }

            private void collectionAttributes(object value, Dictionary<string, object> attr, Type type)
            {

                Dictionary<string, object> o = new Dictionary<string, object>();
                if (value is IDictionary && value.GetType().IsGenericType)
                {
                    attr["type"] = "map";
                    attr["key"] = type.GetGenericArguments()[0].Name;
                    if(type.GetGenericArguments()[1].IsGenericType == true)
                    {
                        collectionAttributes(((IDictionary)value).Values, o, type.GetGenericArguments()[1]);
                        attr["value"] = o;
                    }
                    else
                    {
                        attr["value"] = type.GetGenericArguments()[1].Name;
                    }
                }
                else if (value is ICollection && type.IsGenericType)
                {
                    attr["type"] = "array";
                    if (type.GetGenericArguments()[0].IsGenericType == true)
                    {
                        collectionAttributes(value, o, type.GetGenericArguments()[0]);
                        attr["value"] = o;
                    }
                    else
                    {
                        attr["of"] = type.GetGenericArguments()[0].Name;
                    }
                }
            }
        }

用法

class TrialObject
    {
        [JsonConverter(typeof(TypeInfoConverter))]
        public String szObject = "trial string";

        [JsonConverter(typeof(TypeInfoConverter))]
        public Double doubleObject = 999999999.999;

        [JsonConverter(typeof(TypeInfoConverter))]
        public Boolean boolObject = true;

        [JsonConverter(typeof(TypeInfoConverter))]
        public DateTime dateObject = DateTime.Now;

        [JsonConverter(typeof(TypeInfoConverter))]
        public GeoPoint geoPointObject = new GeoPoint() { Latitude = 123456789.123456, Longitude = 123456789.123456 };

        [JsonConverter(typeof(TypeInfoConverter))]
        public Dictionary<string, string> mapObject = new Dictionary<string, string>();

        [JsonConverter(typeof(TypeInfoConverter))]
        public Dictionary<string, List<GeoPoint>> mapObjectEx = new Dictionary<string, List<GeoPoint>>()
            {{ "1", new List<GeoPoint>() { new GeoPoint() { Latitude = 0.0, Longitude = 0.0 } } }};

        [JsonConverter(typeof(TypeInfoConverter))]
        public List<GeoPoint> points = new List<GeoPoint>()
            { new GeoPoint() { Latitude=0.0, Longitude=0.0 } };

        [JsonConverter(typeof(TypeInfoConverter))]
        public Rating rating = Rating.Good;
    }

    class GeoPoint
    {
        public double Latitude;
        public double Longitude;
    }

    enum Rating
    {
        Good,
        Bad,
    }

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2020-01-26
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-03-18
    • 1970-01-01
    • 2012-04-28
    相关资源
    最近更新 更多