简体   繁体   中英

C# element to create a specifiy json output

from a C# aspnet core controller I produce the following output :

[
{
    "info": {
        "type": 6,
        "message": "a message"
    }
},
{
    "detail": {
        "type": 8,
        "something": "a different thing"
     }
}
]

I'm doing this at the moment with:

Dictionary<string, object> Result = new Dictionary<string, object>();
Result.Add("info", new Info() { Type = 6, message = "a message"});
Result.Add("detail", new Detail() { Type = 8, something = "a different thing" });
JsonConvert.SerializeObject(new object[] { _Errors });

But this won't work when it comes to an output like this:

[
{
    "info": {
        "type": 6,
        "message": "a message"
    }
},
{
    "info": {
        "type": 6,
        "message": "another message"
     }
}
]

Since this would need duplicate keys in the dictionary I have no idea how which structure will produce this output.

KeyValuePair wont work since this results in "key": "info"... and so on. Also a class with several members using the same JsonProperty attribute wont work since the number of elements is dynamic.

I assume what you want is this output:

[
  {
    "Info": {
      "Type": 5,
      "Message": "A message 1"
    }
  },
  {
    "Info": {
      "Type": 6,
      "Message": "A message 3"
    }
  },
  {
    "Info": {
      "Type": 7,
      "Message": "A message 2"
    }
  },
  {
    "Error": {
      "Type": 8,
      "Something": "A different thing"
    }
  }
]

Implement your own JsonConvert, here is an example that provides the above serialization

using System;
using System.Collections.Generic;
using System.Linq;
using Newtonsoft.Json;

namespace JsonSerialization
{
    public class ResultsEntrySerializer : JsonConverter
    {
        public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
        {
            var entry = value as IResultsEntry;
            if (entry == null) throw new ArgumentException("Cannot convert parameter", nameof(value));
            writer.WriteStartObject();
            writer.WritePropertyName(entry.Name);
            serializer.Serialize(writer, entry.Entry);
            writer.WriteEndObject();
        }

        public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
        {
            //TODO: I leave this up to the reader
            throw new NotImplementedException();
        }

        public override bool CanConvert(Type objectType)
        {
            return objectType.GetInterfaces().Contains(typeof(IResultsEntry));
        }
    }

    [JsonConverter(typeof(ResultsEntrySerializer))]
    interface IResultsEntry
    {
        string Name { get; }
        object Entry { get; set; }
    }

    class Info
    {
        public int Type { get; set; }
        public string Message { get; set; }
    }

    class InfoEntry : IResultsEntry
    {
        string IResultsEntry.Name => "Info";

        object IResultsEntry.Entry
        {
            get => Entry;
            set => Entry = (Info)value;
        }

        public Info Entry { get; set; }
    }

    class Detail
    {
        public int Type { get; set; }
        public string Something { get; set; }
    }

    class DetailEntry : IResultsEntry
    {
        string IResultsEntry.Name => "Error";

        object IResultsEntry.Entry
        {
            get => Entry;
            set => Entry = (Detail)value;
        }

        public Detail Entry { get; set; }
    }

    class Program
    {
        static void Main(string[] args)
        {
            var list = new List<IResultsEntry>
            {
                new InfoEntry
                {
                    Entry = new Info { Type = 5, Message = "A message 1" }
                },
                new InfoEntry
                {
                    Entry = new Info { Type = 6, Message = "A message 3" }
                },
                new InfoEntry
                {
                    Entry = new Info { Type = 7, Message = "A message 2" }
                },
                new DetailEntry
                {
                    Entry = new Detail { Type = 8, Something = "A different thing" }
                }
            };
            Console.WriteLine(JsonConvert.SerializeObject(list));
            Console.ReadLine();
        }
    }
}

Also, if you want them lowercase that's trivial too see this Stackoverflow post: Json keys lowercase

sorry for the confusion. After checking the things a bit I found (not really) a solution. I came up with a dictionary which has a special key. Json.Net just uses ToString() for the dictionary keys so something like this seemed to do what I wanted.

    public class DupKey {
    public string Name { get; set; }
    public int Index { get; set; }
    public override string ToString() {
        return Name;
    }
    public override bool Equals(object obj) {
        if(!(obj is DupKey)) return false;
        return Index == ((DupKey)obj).Index;
    }
    public override int GetHashCode() {
        return Index.GetHashCode();
    }
}

But finally I found that this produces a wrong result. Last not least I found a solution (without the need for an own serializer) which gives the correct result.

Since this looks some kind of universal for me I post it here.

    public class DynamicJsonEntry : DynamicObject {
    private string _JsonName;
    public object Element { get; set; }
    public DynamicJsonEntry(string pJsonName, object pElement) : this(pJsonName) {
        Element = pElement;
    }
    public DynamicJsonEntry(string pJsonName) {
        if(string.IsNullOrWhiteSpace(pJsonName)) {
            throw new ArgumentNullException(nameof(pJsonName));
        }
        _JsonName = pJsonName;
    }
    public override IEnumerable<string> GetDynamicMemberNames() {
        yield return _JsonName;
        foreach(var prop in GetType().GetProperties().Where(a => a.CanRead && a.GetIndexParameters().Length == 0 && a.Name != nameof(Element))) {
            yield return prop.Name;
        }
    }
    public override bool TryGetMember(GetMemberBinder pBinder, out object pResult) {
        if(pBinder.Name == _JsonName) {
            pResult = Element;
            return true;
        }
        return base.TryGetMember(pBinder, out pResult);
    }
}

Now I can add any entry with the name I want simply like this:

List<DynamicJsonEntry> lRet = new List<DynamicJsonEntry>();
lRet.Add(new DynamicJsonEntry("info", new Info() { Type = 6, message = "a message" }));
lRet.Add(new DynamicJsonEntry("detail", new Detail() { Type = 8, something = "a different thing" }));
lRet.Add(new DynamicJsonEntry("info", new Info() { Type = 6, message = "a second message" }));

Thank you Joachim for your help.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM