skip to Main Content

I have a problem with the default Serialization of my object.

public interface IUserProduct
{
    public int Gramature { get; set; }
    public ImageSource ProductImage { get; }
    public string Name { get; }
    public decimal Kcal { get; }
    public decimal Carbohydrates { get; }
    public decimal Fat { get; }
    public decimal Proteins { get; }
    public IProductDataTemplate ProductTemplate { get; set; }
}

The object has a property ImageSource, unfortunately it is referring to itself, and I’m getting a System.Text.Json.JsonException: 'A possible object cycle was detected. exception.

I have built a custom converter that is replacing a ImageSource with a string but Im still getting the exception.

public override void Write(Utf8JsonWriter writer, IList<IUserProduct> value, JsonSerializerOptions options)
{
    writer.WriteStartArray();
    foreach (IUserProduct product in value)
    {
        writer.WriteStartObject();
        writer.WriteString(nameof(product.Name), product.Name);
        writer.WriteNumber(nameof(product.Kcal), product.Kcal);
        writer.WriteNumber(nameof(product.Gramature), product.Gramature);
        writer.WriteNumber(nameof(product.Proteins), product.Proteins);
        writer.WriteNumber(nameof(product.Carbohydrates), product.Carbohydrates);
        writer.WriteNumber(nameof(product.Fat), product.Fat);
        string image = product.ProductImage.ToString();
        writer.WriteString(nameof(product.ProductImage), image);
        writer.WriteEndObject();
    }
    writer.WriteEndArray();
}

I have tried adding ReferenceHandler.Preserve and ReferenceHandler.IgnoreCycles but it doesn’t work neither.

This is a method that throws the exception:

private static readonly JsonSerializerOptions ProductSerializer = new JsonSerializerOptions() 
{
    Converters = { new JsonProductCustomConverter() }//this is my custom converter
};

private static void WriteProductsJSON(IUserProduct product, string fileName)
{
    string json;
    List<IUserProduct> products = new List<IUserProduct>();
    if (File.Exists(Path.Combine(DataPath, fileName)))
    {
        json = File.ReadAllText(Path.Combine(DataPath, fileName));
        products = System.Text.Json.JsonSerializer.Deserialize<List<IUserProduct>>(json, options: ProductSerializer);
    }
    products.Add(product);
    json = System.Text.Json.JsonSerializer.Serialize(products, options: ProductSerializer);
    File.WriteAllText(Path.Combine(DataPath, fileName), json);
}

What am I doing wrong? How can I serialize this ImageSource as a string and remove the exception?

2

Answers


  1. Chosen as BEST ANSWER

    The problem was lying in the JsonConverter I had made, I did not understand how it worked correctly. CustomJsonConverter that inherits from JsonConverter<T> is not passing the whole object meant for serialization to the public override void Write(Utf8JsonWriter writer, IList<IUserProduct> value, JsonSerializerOptions options) it only passes individual properties of that object to the method.

    In order to target a single property of type T to conversion, the type of that property should be specified in a custom-made converter. So, to custom serialize a ImageSource as a string the converter should look like this in my case:

    public override void Write(Utf8JsonWriter writer, ImageSource value, JsonSerializerOptions options)
    {
        if (value as FileImageSource != null)
        {
            FileImageSource image = value as FileImageSource;
            writer.WriteStringValue(image.File.ToString());
        }
        else if (value as UriImageSource != null)
        {
            UriImageSource image = value as UriImageSource;
            writer.WriteStringValue(image.Uri.ToString());
        }
    }
    

  2. Why don’t you add another property for the string and mark ProductImage with [JsonIgnore]?

    public string ProductImageString { get; set; }
    
    [JsonIgnore]
    public ImageSource ProductImage
    {
        get => ImageSource.FromString(ProductImageString);
        set => ProductImageString = value.ToString();
    }
    

    Note: get with ImageSource.FromString is just an example. Use whatever method you have if you want to read the ImageSource back from your string.

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search