Reputation: 424
I have a problem whereby I wish to generate a JSON field where the field name is known at runtime e.g.:
{ "known_at_run_time": ["test","test","test"] }
So I tried implementing it this way, yet whenever I run my unit test I get an error saying that that my custom JsonConverter cannot be created. Here is my code:
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;
}
}
What am I doing wrong? Any information will help.
Upvotes: 0
Views: 1405
Reputation: 129667
First, try removing the [JsonConverter]
attribute from the Query
property of your TermFilter
class. You don't need it because the Query
property is a JsonTuple
, and you are already passing an instance of your JsonTupleConverter
to the JsonConvert.SerializeObject()
method inside your ToJson()
method, specifying that it can handle JsonTuples
. This will get rid of the error.
However, there is still another issue. It seems that your intent is to get the Query
property to serialize to JSON, but as things stand now this will not happen. This is because you have marked your TermFilter
class with [JsonObject(MemberSerialization.OptIn)]
, and the Query
property lacks a [JsonProperty]
attribute to signal that you want that property to be included in the output. You will need to add [JsonProperty("query")]
to fix that. Once you have done that, you should get the output you expect.
As an aside, you don't need to catch exceptions if you only intend to throw them again without doing anything else with them. I see that pattern everywhere in your code. Instead, just leave out the try/catch altogether; it does exactly the same thing and will make your code much more concise. Only catch an exception if you intend to handle it.
Upvotes: 1
Reputation: 56536
I think that your exception is occurring because JsonTupleConverter
doesn't have a parameterless constructor.
public JsonTupleConverter() { }
If you add that, the error should go away, but your code might not work because then it'd probably be trying to use a converter without the types set up correctly.
Maybe you should just be serializing it as a dictionary? 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"]}
Upvotes: 1