0

I want to deserialize this json data to Result class and put 3 book values(FirstBook, SecondBook, ThirdBook) into IEnumerable< Book >:

[
  {      
    "Id": 1,
    "FirstBook": "B1",
    "SecondBook": "B2",
    "ThirdBook": "B3",
    ...    
  },
  {
    "Id": 2,
    "FirstBook": "BB1",
    "SecondBook": "BB2",
    "ThirdBook": "BB3",
    ...
  }
]

Result Class:

public class Result
{
  public int Id { get; set;}

  [JsonConverter(typeof(BooksJsonConverter))]
  public IEnumerable<Book> Books { get; set; }
}

Book class:

public class Book
{
    public string Name { get; set; }
}

Deserialize it to class Result that contain IEnumerable< Book >

JsonConvert.DeserializeObject<IEnumerable<Result>>(json);

I think i should create JsonConverter:

public class BooksJsonConverter : JsonConverter<IEnumerable<Book>>
{
    public override IEnumerable<Book> ReadJson(JsonReader reader, Type objectType, IEnumerable<Book> existingValue, bool hasExistingValue, JsonSerializer serializer)
    {
        //What should i write here?
        return new List<Book>();
    }

    public override void WriteJson(JsonWriter writer, Book value, JsonSerializer serializer)
    {
        throw new NotImplementedException();
    }
}

In this situation ReadJson of BooksJsonConverter not called but when i add JsonProperty attrib it called but i don't know what should i write inside ReadJson:

public class Result
{
  public int Id { get; set;}

  [JsonConverter(typeof(BooksJsonConverter))]
  [JsonProperty("FirstBook")]
  public IEnumerable<Book> Books { get; set; }
}

Question is how should i convert FirstBook, SecondBook, ThirdBook from json to IEnumerable< Book > inside of result class?

Noob
  • 23
  • 1
  • 7

2 Answers2

0

Since you did not provide the Book class structure, I customized it to include a BookName field as follow.

   public class Book
    {
        public string BookName { get; set; }
    }



[JsonConverter(typeof(CustomeJsonConverter))]
public class Result
{
    public int Id { get; set; }
    public IEnumerable<Book> Books { get; set; }
}

Update

Ok, jsonconvertercan achieve it, the method is roughly the same, but you need to add a custom jsonconverter attribute to Result class rather than Book, where the Book class will be converted and added to the result.

Here is the code:

 public class HomeController : Controller
    {
        public IActionResult Test()
        {
            string json = @" [
    {
      'Id': 1,
      'FirstBook': 'B1',
      'SecondBook': 'B2',
      'ThirdBook': 'B3'
    },
    {
      'Id': 2,
      'FirstBook': 'BB1',
      'SecondBook': 'BB2',
      'ThirdBook': 'BB3'
    }
  ] ";
            var xx = JsonConvert.DeserializeObject<IEnumerable<Result>>(json,new CustomeJsonConverter());
            return Ok();
        }

    }


public class CustomeJsonConverter : JsonConverter
    {
        public override bool CanConvert(Type objectType)
        {
            return System.Attribute.GetCustomAttributes(objectType).Any(v => v is KnownTypeAttribute);
        }
        public override bool CanWrite
        {
            get { return false; }
        }

        public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
        { 
            // Load JObject from stream
            JObject jObject = JObject.Load(reader);
            Result result = new Result();
            Dictionary<string, string> dictObj = jObject.ToObject<Dictionary<string, string>>();
            List<Book> books = new List<Book>();
            foreach (var obj in dictObj)
            {
                if (obj.Key.Contains("Book"))
                {
                    Book book = new Book()
                    {
                        Name = obj.Value
                    };
                    books.Add(book);
                }
            }
            result.Id = Convert.ToInt32(dictObj["Id"]);
            result.Books = books.AsEnumerable();
            return result; 
        }


        public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
        {
            throw new NotImplementedException();
        }

    }

You can also refer to this.

LouraQ
  • 6,443
  • 2
  • 6
  • 16
  • I don't want to create static class for it, i think using custom jsonconverter is better. Do u know how can i do it with JsonConverter? – Noob Apr 30 '20 at 10:30
  • @Noob, I have updated my post which is followed your requirement, hope it can help you. – LouraQ May 01 '20 at 03:06
  • Yes it works ty, but what is this code after return? `throw new SerializationException`. is it work after return? – Noob May 01 '20 at 06:42
  • @Noob, this is used to catch exceptions. If the conversion fails, the exception will be thrown , you can delete it which will not affect the results. – LouraQ May 01 '20 at 07:25
  • You write throw after return statement, how can it execute after return? – Noob May 01 '20 at 08:39
  • @Noob, I think this cannot be verified in my method(I have moved this line in my post). In this link:https://stackoverflow.com/a/26235882/12884742, if there are some errors during the serializer, an exception will be thrown directly instead of returning the collection. – LouraQ May 01 '20 at 09:03
0

You can use JsonExtensionData that allows you to serialize elements of a JSON document which does not have matching properties on the destination object:

 public class Result
    {
        public int Id { get; set; }

        public IEnumerable<Book> Books { get; set; }

        [JsonExtensionData]
        private IDictionary<string, JToken> _extensionData;

        [OnDeserialized]
        private void OnDeserialized(StreamingContext context)
        {
            Books = _extensionData
             .Where(d => d.Key.Equals("FirstBook") || d.Key.Equals("SecondBook"))
             .Select(b => new Book { Name = b.Value.ToString() });
        }
    }
AliD32
  • 111
  • 5