Json.Net在创建自定义JsonConverter时出错

时间:2014-01-09 16:24:42

标签: c# json.net

我有一个问题,我希望生成一个JSON字段,其中字段名称在运行时已知,例如:

{ "known_at_run_time": ["test","test","test"] }

所以我尝试以这种方式实现它,但每当我运行我的单元测试时,我得到一个错误,说我的自定义JsonConverter无法创建。这是我的代码:

TermFilter.cs

public enum ExecutionType { plain, fielddata, @bool, and, or }

[JsonObject(MemberSerialization.OptIn)]
public class TermFilter
{
    #region PROPERTIES
    private JsonTuple query;

    private ExecutionType execution;
    private string _execution;

    private bool _cache;
    #endregion

    #region CONSTRUCTOR
    public TermFilter()
    {
        try
        {
            this.query = null;
            this.Execution = ExecutionType.plain;
            this.Cache = true;
        }
        catch(Exception)
        {
            throw;
        }
    }

    public TermFilter(ExecutionType execution)
        : this()
    {
        try
        {
            this.Execution = execution;
        }
        catch (Exception)
        {
            throw;
        }
    }

    public TermFilter(ExecutionType execution, bool cache)
        : this(execution)
    {
        try
        {
            this.Cache = cache;
        }
        catch (Exception)
        {
            throw;
        }
    }

    public TermFilter(string field, string[] terms)
        :this()
    {
        try
        {
            this.Query = new JsonTuple(field, new HashSet<string>(terms));
        }
        catch (Exception)
        {
            throw;
        }
    }
    #endregion

    #region GET/SET

    //[JsonProperty(ItemConverterType = typeof(JsonTupleConverter))]
    //[JsonProperty]
    [JsonConverter( typeof(JsonTupleConverter) )]
    public JsonTuple Query
    {
        get { return query; }
        set { query = value; }
    }

    public ExecutionType Execution
    {
        get { return execution; }
        set 
        { 
            execution = value;
            _execution = value.ToString();
        }
    }

    [JsonProperty(PropertyName = "execution")]
    public string _Execution
    {
        get { return _execution; }
        set { _execution = value; }
    }

    [JsonProperty(PropertyName = "_cache")]
    public bool Cache
    {
        get { return _cache; }
        set { _cache = value; }
    }
    #endregion

    #region METHODS
    public TermFilter AddTerm(string term)
    {
        try
        {
            if (!this.query.Data.Contains(term))
                this.query.Data.Add(term);

            return this;
        }
        catch (Exception)
        {
            throw;
        }
    }

    public string ToJson()
    {
        try
        {
            var settings = new JsonSerializerSettings();
            settings.TypeNameHandling = TypeNameHandling.Objects;
            settings.Converters.Add(new JsonTupleConverter(new Type[] { typeof(JsonTuple) }));
            settings.NullValueHandling = NullValueHandling.Ignore;

            return JsonConvert.SerializeObject(this, settings);

            //return JsonConvert.SerializeObject(this, Formatting.None, new JsonTupleConverter(typeof(JsonTuple)));
            //return JsonConvert.SerializeObject(this, Formatting.None, new JsonConverter[] { new JsonTupleConverter(typeof(JsonTuple)),  });

        }
        catch (Exception)
        {
            throw;
        }
    }
    #endregion
}

JsonTuple.cs

public class JsonTuple
{
    #region PROPERTIES
    private string field;
    private HashSet<string> data;
    #endregion

    #region CONSTRUCTOR
    public JsonTuple()
    {
        try
        {
            this.field = null;
            this.data = null;
        }
        catch (Exception)
        {
            throw;
        }
    }

    public JsonTuple(string field, HashSet<string> data)
    {
        try
        {
            this.field = field;
            this.data = data;
        }
        catch (Exception)
        {
            throw;
        }
    }
    #endregion

    #region GET/SET
    public string Field
    {
        get { return field; }
        set { field = value; }
    }

    public HashSet<string> Data
    {
        get { return data; }
        set { data = value; }
    }
    #endregion

    #region METHODS
    public string ToJson()
    {
        try
        {
            return JsonConvert.SerializeObject(this, Formatting.None, new JsonTupleConverter(typeof(JsonTuple)));
        }
        catch (Exception)
        {
            throw;
        }
    }
    #endregion
}

JsonTupleConverter.cs

public class JsonTupleConverter : JsonConverter
{
    private readonly Type[] _types;

    public JsonTupleConverter(params Type[] types)
    {
        try
        {
            _types = types;
        }
        catch (Exception)
        {
            throw;
        }
    }

    public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
    {
        try
        {
            JToken t = JToken.FromObject(value);

            if (t.Type != JTokenType.Object)
            {
                t.WriteTo(writer);
            }
            else if (!_types.Any(_t => _t == value.GetType()))
            {
                serializer.Serialize(writer, value);
            }
            else
            {
                JsonTuple tuple = (JsonTuple)value;
                if ((tuple != null) && (tuple.Field != null) && (tuple.Data != null))
                {
                    JToken entityToken = null;
                    if (tuple.Data != null)
                        entityToken = JToken.FromObject(tuple.Data);

                    JObject o = new JObject();
                    o.AddFirst(new JProperty(tuple.Field, entityToken));
                    o.WriteTo(writer);
                }
            }
        }
        catch (Exception)
        {
            throw;
        }
    }

    public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
    {
        throw new NotImplementedException("Unnecessary because CanRead is false. The type will skip the converter.");
    }

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

    public override bool CanConvert(Type objectType)
    {
        return _types.Any(t => t == objectType);
    }
}

test.cs中

[TestMethod]
public void TermFieldSertialization()
{
    try
    {
        TermFilter filter = new TermFilter("test.field", new string[] {"test1", "test2", "test3"});
        Assert.IsNotNull(filter);

        string sampleJson = filter.ToJson();
        Assert.IsNotNull(sampleJson);
    }
    catch (Exception)
    {
        throw;
    }
}

我做错了什么?任何信息都会有所帮助。

2 个答案:

答案 0 :(得分:1)

我认为您的异常正在发生,因为JsonTupleConverter没有无参数构造函数。

public JsonTupleConverter() { }

如果你添加它,错误应该消失,但你的代码可能无法正常工作,因为它可能会尝试使用没有正确设置类型的转换器。

也许您应该将其序列化为字典? E.g。

var myDict = new Dictionary<string, List<string>>
{
    { "known_at_run_time", new List<string> { "test","test","test" } }
};
string ser = JsonConvert.SerializeObject(myDict);
// ser is {"known_at_run_time":["test","test","test"]}

答案 1 :(得分:1)

首先,尝试从[JsonConverter]类的Query属性中删除TermFilter属性。您不需要它,因为Query属性是JsonTuple,并且您已经将JsonTupleConverter的实例传递到JsonConvert.SerializeObject()内的ToJson()方法方法,指定它可以处理JsonTuples。这将摆脱错误。

然而,还有另一个问题。看来你的目的是让Query属性序列化为JSON,但现在情况就不会发生了。这是因为您已使用TermFilter标记了[JsonObject(MemberSerialization.OptIn)]类,并且Query属性缺少[JsonProperty]属性,表示您希望该属性包含在输出中。您需要添加[JsonProperty("query")]来解决此问题。完成后,您应该得到您期望的输出。

顺便说一句,如果你只打算再次抛出它们而不对它们做任何其他事情,你就不需要捕获异常。我在你的代码中到处都看到了这种模式。相反,只需完全省略try / catch;它完全相同,将使您的代码更简洁。如果您打算处理它,只能捕获异常。