How to remove a property during WriteJson

I created a test console application that has a simple class called Other. As an exercise, I want to set the OtherString property to null when it is serialized. I know how to do this with a special ContractResolver. I also need to do this using my own converter.

The first converter I wrote was simple and as I thought it should be. However, it will throw a "Self-reference loop detected using the type" JsonContractandConvert.Models.Other ". Path ''." an exception. After some reading, I made some changes, and now I have a working converter. These changes are much more complicated, but they work.

My question is why these changes are necessary, and is there a better way to do this with a converter?

Another class:

[JsonConverter(typeof(OtherConverter))]
public class Other
{
    public int Id { get; set; }
    public string OtherString { get; set; }
    public int OtherInt { get; set; }

public string OtherName
    {
        get
        {
        return "Other Name = " + this.OtherString;
        }
    }
}

First attempt: (This throws an exception)

public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
    if (!this.CanConvert(value.GetType())) return;

    var entity = value as Other;
    if (entity == null) return;

    entity.OtherString = null;

    serializer.Serialize(writer, entity);
}

Second attempt: (This works as expected)

public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
    if (!this.CanConvert(value.GetType())) return;

    var entity = value as Other;
    if (entity == null) return;

    entity.OtherString = null;

    writer.WriteStartObject();
    var props = entity.GetType().GetProperties();
    foreach (var propertyInfo in props)
    {
        var ignorAttribute =
            propertyInfo.CustomAttributes.FirstOrDefault(i => i.AttributeType == typeof(JsonIgnoreAttribute));

    if (ignorAttribute != null) continue;
                var tempVal = propertyInfo.GetValue(entity);
            if (tempVal == null) continue;

            writer.WritePropertyName(propertyInfo.Name);
            serializer.Serialize(writer, tempVal);
        }
    }
    writer.WriteEndObject();
}

Edit:

Here is the code from the console application that I use for testing.

class Program
{
    static void Main(string[] args)
    {
        var otherObj = new Other { Id = 123, OtherInt = 456, OtherString = "This is the other string"};
        var json = JsonConvert.SerializeObject(otherObj, Formatting.Indented);
        Console.WriteLine(json);
    }
}
+3
source share
1 answer

Answering my own questions ... (I think I finally hugged Json Converters)

To answer my basic question about why one of the methods works and the other does not. I think the answer is that a method that does not work is simply incorrect. From what I can say, you need to use a writer object. If you do not, it will not work. (There may be scenarios where this is not the case, but I never found it.)

Json, [JsonIgnore]. ( ?)

- , . , , - . , , String, Int, . , , .

:

:

public class Account
{
    public int Id { get; set; }
    public string AccountName { get; set; }

    [JsonIgnore]
    public virtual Account DefaultAssignTo { get; set; }
    public int? DefaultAssignToId { get; set; }

    [JsonIgnore]
    public virtual ICollection<Role> Roles { get; set; }

    [JsonIgnore]
    public virtual Other Other { get; set; }
    public int? OtherId { get; set; }

    [JsonConverter(typeof(StringConverter))]
    public string OtherName
    {
        get
        {
                return "Name = " + this.AccountName;
        }
    }

}

public class StringConverter : JsonConverter
{        
    public override bool CanConvert(Type objectType)
    {
        return typeof(string).IsAssignableFrom(objectType);
    }

    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)
    {
        if (!this.CanConvert(value.GetType())) return;

        writer.WriteValue("blah blah blah");    
    }
}

: ( , Entity Framework )

using (var db = new Context())
{
    var json = JsonConvert.SerializeObject(db.Accounts.FirstOrDefault(), Formatting.Indented, 
        new JsonSerializerSettings { NullValueHandling = NullValueHandling.Ignore });
    Console.WriteLine(json);
}

:

{
  "Id": 43,
  "AccountName": "John",
  "DefaultAssignToId": 43,
  "OtherId": 19,
  "OtherName": "blah blah blah"
}

, , - writer.WriteStartObject(). , , . , . OP , . - Json, , . , , . , , .

:

writer.WriteValue("blah blah blah");

:

writer.WriteStartObject();
writer.WritePropertyName("BlahProp");
serializer.Serialize(writer, "blah blah blah");
writer.WriteEndObject();

, : ( , stringNameName )

{
  "Id": 43,
  "AccountName": "John",
  "DefaultAssignToId": 43,
  "OtherId": 19,
  "OtherName": {
    "BlahProp": "blah blah blah"
  }
}

. : , -, Json, Role, .

using BaseCollection = System.Collections.Generic.ICollection<JsonContractandConvert.Models.Role>;
public class RemoveAccountsFromRolesConverter : JsonConverter
{
    public override bool CanConvert(Type objectType)
    {
        return typeof(BaseCollection).IsAssignableFrom(objectType);
    }

    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)
    {
        if (!this.CanConvert(value.GetType())) return;

        var entities = value as BaseCollection;
        if (entities == null) return;

        writer.WriteStartArray();
        foreach (var entity in entities)
        {
        entity.Accounts = null;
        serializer.Serialize(writer, entity);
        }
        writer.WriteEndArray();
    }
}

, , :

. , , , .

public class ShallowCopyCollectionConverter<TCollectionType, TCopyType> : JsonConverter
    where TCollectionType : IEnumerable<TbdEntity>
    where TCopyType : TbdEntity, new()
{
    public override bool CanConvert(Type objectType)
    {
        return typeof(TCollectionType).IsAssignableFrom(objectType);
    }

    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)
    {
        if (this.CanConvert(value.GetType()) == false) return;

        var entities = (TCollectionType)value;
        writer.WriteStartArray();
        foreach (var entity in entities)
        {
            serializer.Serialize(writer, entity.ShallowCopy<TCopyType>()); //ShallowCopy<> is a method in the base class that all of my classes extend.
        }
        writer.WriteEndArray();
    }
}

:

public class DataSnapInConverter : JsonConverter
{
    public override bool CanConvert(Type objectType)
    {
        return typeof(DataSnapIn) == (objectType);
    }

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

    public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
    {
        if (this.CanConvert(objectType) == false) return null;

        var jo = JObject.Load(reader);
        var typeName = jo["snapInType"] ?? jo["SnapInType"];  //the abstract classes have this property to identify what concrete class they are.
        var typeNameString = typeName.ToString();

        var deserializeType = Type.GetType(typeNameString);
        if(deserializeType == null)
            throw new Exception("SnapInType is null or does not reference a valid class.");

        var result = Activator.CreateInstance(deserializeType);
        serializer.Populate(jo.CreateReader(), result);

        return result;
    }

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

All Articles