我有一个问题,我希望生成一个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;
}
}
我做错了什么?任何信息都会有所帮助。
答案 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;它完全相同,将使您的代码更简洁。如果您打算处理它,只能捕获异常。