Question

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:

  • Use IDataContractSurrogate and supply a deserialized surrogate of the JObject
  • Use IClientMessageFormatter to create custom serialization/deserialization
  • Somehow else configure/override the de/serialization 'system' to use a Newtonsoft serializer instead of or along with DataContractSerializer.

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?

Was it helpful?

Solution 2

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.

OTHER TIPS

I think, KnownType will not help - since it should only help to "find" the type, when wcf DataContractSerializer should know how to serialize.

And it does not, because JObject does not include DataContract and DataMember attributes.

If JObject is xml serializable, I'd consider to use xml serialization instead of DataContract serialization.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top