Update - The problem I had here was user error. See my answer below.
I have an class which I want to add to the DataContract of a WCF API which should be "binding agnostic"; i.e., it should be able to support multiple bindings, including BasicHttp and NamedPipe. The class I want to add to the data contract uses a Newtonsoft.Json.Linq.JObject to store the member data:
[DataContract]
public class MyFacet
{
public MyFacet(string facetName)
{
Initialize();
FacetName = facetName;
}
internal JObject JsonObject { get; private set; }
[OnDeserializing] // required for deserialization to init JsonObject
private void OnDeserializing(StreamingContext c)
{
Initialize();
}
private void Initialize()
{
JsonObject = new JObject();
}
[DataMember]
public string FacetName
{
get
{
return GetProperty<string>("facet");
}
private set
{
SetProperty("facet", value);
}
}
public string ToJson()
{
return JsonObject.ToString(Newtonsoft.Json.Formatting.None);
}
public T GetProperty<T>(string key)
{
JToken value;
if (JsonObject.TryGetValue(key, out value))
{
return value.Value<T>();
}
return default(T);
}
public void SetProperty(string name, object value)
{
SetProperty(name, new JValue(value));
}
internal void SetProperty(string name, JToken value)
{
JsonObject[name] = value;
}
}
For a variety of reasons I would like to keep this class as is, if at all possible. When I try to serialize this class with DataContractSerializer (in a test app), I get the following error:
System.Runtime.Serialization.SerializationException:
Type 'ConsoleTest.MyFacet' with data contract name
'MyFacet:http://schemas.datacontract.org/2004/07/ConsoleTest' is not expected.
Consider using a DataContractResolver or add any types not known statically to
the list of known types - for example, by using the KnownTypeAttribute attribute
or by adding them to the list of known types passed to DataContractSerializer.
Which, essentially, makes sense to me. But, adding the various Newtonsoft types as KnownTypes doesn't work -- this leads to a "recursive collection data contract" error. I think I would have the same problem if I tried to implement a DataContractResolver.
I believe I can serialize the JObject to XML using a Newtonsoft serializer. It seems like I have a few options:
And, I'm worried about having to support various bindings. The named pipe used binary encoding, and I'm not sure how that impacts this serialization issue -- maybe it means I must use a surrogate?
Turns out this was %100 user error. In my serialization test harness, I was passing the wrong type when I was creating the DataContractSerializer. The following demonstrates the ERROR:
Console.WriteLine("serializing...");
MyFacet facet = new MyFacet("MyFacet");
// On the following line, I was passing the wrong type to the ctor.
// Needed to pass "MyFacet" here, not "Person"
//!ERROR
DataContractSerializer serializer = new DataContractSerializer(typeof(Person));//!ERROR
var settings = new XmlWriterSettings { Indent = true };
using (var writer = XmlWriter.Create(fileName, settings))
serializer.WriteObject(writer, facet);
Once I fixed the error, the class in the original post serialized just fine. The error message I was originally getting really threw me off. I was assuming that there was something about JObject or one of the Newtonsoft types causing the issue, when in fact, I clearly wasn't even trying to serialize any of those types.