I'm using Newtonsoft JSON library and I'm trying to deserialize a JSON. The problem is that when I use [JsonConverter(typeof(StringEnumConverter))] I get this error: Cannot apply attribute class 'JsonConverter' because it is abstract.
Here are my classes:
public class ActionRepository
{
[JsonConverter(typeof(StringEnumConverter))]
public enum AllowedActions
{
FINDWINDOW,
}
public enum AllowedParameters
{
WINDOWNAME,
}
}
public class Action
{
public AllowedActions Name { get; set; }
public List<Parameter> Parameters { get; set; }
}
I get the squiggly line under the JsonConverter.
EDIT: The JsonConverter class is indeed abstract if I navigate to the class (ctrl+click in VS). I'm using .NET for Windows Universal.
The problem appears to be that when not targeting a .Net framework application - the JsonConverter class is marked as abstract.
The solution looks to be to use JsonConvert as an alternative.
Related
I have data that is best described as "onion-like" in that each outer layer builds on the one below it. Below you will see a vastly simplified version (mine is several layers deeper but exhibits the same behavior at each level).
[CollectionDataContract]
public abstract class AbstractTestGroup : ObservableCollection<AbstractTest>
{
[DataMember]
public abstract string Name { get; set; }
}
[CollectionDataContract]
[KnownType(typeof(Test))]
public class TestGroup : AbstractTestGroup
{
public override string Name
{
get { return "TestGroupName"; }
set { }
}
[DataMember]
public string Why { get { return "Why"; } set { } }
}
[DataContract]
public abstract class AbstractTest
{
[DataMember]
public abstract string SayHello { get; set; }
}
[DataContract]
public class Test : AbstractTest
{
//Concrete class - members in this class get serialized
[DataMember]
public string Month { get { return "June"; } set { } }
public override string SayHello { get { return "HELLO"; } set { } }
}
I create an instance of TestGroup and add Test objects to it using the .Add that comes with the ObservableCollection.
When I serialize and de-serialize this structure I get the following
<TestGroup xmlns="http://schemas.datacontract.org/2004/07/WpfApplication2" xmlns:i="http://www.w3.org/2001/XMLSchema-instance">
<AbstractTest i:type="Test">
<SayHello>HELLO</SayHello>
<Month>June</Month>
</AbstractTest>
</TestGroup>
The output has left off the DataMembers in TestGroup. As I get deeper in my onion, no DataMembers that are higher are included (even from the abstract classes). I have tried adding [KnownType(typeof(TestGroup))] to both TestGroup and AbstractTestGroup without success.
The question: Why am I not able to serialize the DataMember Why in the TestGroup class?
Follow up question: Is there an alternative way to serialize and de-serialize a structure of this shape? I am planning on using the output locally to "load" the configuration the user specifies. I would prefer to not have to specify my own Serialization scheme if I can avoid it.
For those interested here is how I am generating the class, serializing, and de-serializing it.
TestGroup tg = new TestGroup();
tg.Add(new Test());
DataContractSerializer ser = new DataContractSerializer(typeof(TestGroup));
MemoryStream memoryStream = new MemoryStream();
ser.WriteObject(memoryStream, tg);
memoryStream.Seek(0, SeekOrigin.Begin);
string str;
using (StreamReader sr = new StreamReader(memoryStream))
str = sr.ReadToEnd();
Edit: For what it's worth I tried changing to using Serializable instead and have the same issue.
The reason why the property Why is not serialized is because TestGroup is a collection. And DataContract treats collections specially. The end result is that only the data in the collection is stored and none of the properties are stored.
Lists are stored in a way that any other list could read them in. The only differentiation is between collections and dictionaries. A good reference is http://msdn.microsoft.com/en-us/library/aa347850%28v=vs.110%29.aspx
UPDATE: I've seen some things online that may help you. In particular, change the abstract class attribute declarations to the following:
[DataContract]
[KnownTypes(typeof(Test))]
public abstract class AbstractTest { /* ... */ }
You could have a look at the documentation at MSDN on the KnownTypesAttribute. Apparently, there's also a constructor overload that takes a string that resolves to a method name that would be found via reflection and would be called by the DataContractSerializer to determine the known types for a base class (if you had multiple known types and/or possibly needed to dynamically return known types that may not be known at compile time). There's also web.config XML configurations for setting up known types.
UPDATE: I noticed that the KnownTypesAttribute attribute seems to be misused in the code examples in the OP. So, I wanted to elaborate the above with the full code that should work.
[CollectionDataContract]
[KnownTypes(typeof(TestGroup))] // Need to tell DCS that this class's metadata will be included with members from this abstract base class.
public abstract class AbstractTestGroup : ObservableCollection<AbstractTest>
{
[DataMember]
public abstract string Name { get; set; }
}
[CollectionDataContract]
//[KnownTypes(typeof(Test))] -- You don't need this here....
public class TestGroup : AbstractTestGroup
{
[DataMember] // Even though this is a derived class, you still need to tell DCS to serialize this overridden property when serializing this type
public override string Name
{
get { return "TestGroupName"; }
set { }
}
[DataMember]
public string Why { get { return "Why"; } set { } }
}
[DataContract]
[KnownTypes(typeof(Test))] // Again, you need to inform DCS
public abstract class AbstractTest
{
[DataMember]
public abstract string SayHello { get; set; }
}
[DataContract]
public class Test : AbstractTest
{
//Concrete class - members in this class get serialized
[DataMember]
public string Month { get { return "June"; } set { } }
[DataMember] // Even though this is a derived class, you still need to tell DCS to serialize this overridden property when serializing this type
public override string SayHello { get { return "HELLO"; } set { } }
}
See the comments next to the KnownTypesAttribute attributes in the example above.
UPDATE: Added the DataMemberAttribute attribute to the derived class' overridden properties.
UPDATE: OK, there may be an added dimension to this that is causing the behavior you're referencing. Do you have an interface or a class that is decorated with the ServiceContractAttribute attribute, where the service contains a method which returns one of these abstract types above? If so, then you also need to decorate said interface or class method that returns the abstract type with the ServiceKnownTypesAttribute attribute. A quick and dirty example follows:
[ServiceContract]
//[ServiceKnownTypes(typeof(TestGroup))] -- You could also place the attribute here...not sure what the difference is, though.
public interface ITestGroupService
{
[OperationContract]
[ServiceKnownTypes(typeof(TestGroup))]
AbstractTestGroup GetTestGroup();
}
HTH.
Getting an error when trying to retrieve objects from mongodb:
InvalidOperationException: Can't compile a NewExpression with a
constructor declared on an abstract class
My class is:
public class App
{
public List<Feature> Features { get; set; }
}
public abstract class Feature
{
public string Name { get; set; }
}
public class ConcreteFeature : Feature
{
public string ConcreteProp { get; set; }
}
Not sure why it is having issues with abstractions. I see, mongodb recorded _t: "ConcreteFeature" type name, it has everything to deserialize it. I have no constructor in abstract class.
Ideas?
I needed to list "KnownTypes" for BsonClassMap to make it work:
BsonClassMap.RegisterClassMap<Feature>(cm => {
cm.AutoMap();
cm.SetIsRootClass(true);
var featureType = typeof(Feature);
featureType.Assembly.GetTypes()
.Where(type => featureType.IsAssignableFrom(type)).ToList()
.ForEach(type => cm.AddKnownType(type));
});
This way you won't need to touch the code even if you add new types as long as they are in 1 assembly. More info here.
1.On Abstract class Use
[BsonDiscriminator(Required = true)]
[BsonKnownTypes(typeof(ConcreteFeature)]
public abstract class Feature
{
public string Name { get; set; }
}
public class ConcreteFeature : Feature
{
public string ConcreteProp { get; set; }
}
You're never going to store your abstract class directly in the database. The whole known types stuff is just if you need the inheritance tree in the type discriminator which is typically overkill. The serializer does need to know about your concrete classes in advance though so the below will suffice.
BsonClassMap.RegisterClassMap<ConcreteFeature>();
Assuming you're going to be adding child classes regularly then you can use reflection and register them that way.
I'm setting up a WCF interface. My data contract looks like this:
[DataContract(Namespace = "wcf")]
[KnownType(typeof(TypeFromLibrary))]
public class MyClass
{
[DataMember]
public TypeFromLibrary myProp { get; set; }
}
Is it possible to use TypeFromLibrary in a DataContract like this? All of the examples I have seen only use int, string, double, etc as the property type.
NOTE: I have the copies of the library on both the server and client side of the WCF.
I just wanted to post what worked for me in case someone else was having trouble.
[DataContract(Namespace = "wcf", IsReference = true)]
public class NewType : TypeFromLibrary
{
public NewType(TypeFromLibrary baseObj)
{
//set all props here
}
public event EventHandler<SomeEventArgs> SomeEvent;
[OperationContract]
public string SomeBaseFunction()
{
}
[DataMember]
public CustomBaseProp SomeProp { get; set; }
[DataMember]
public int SomeInt { get; set;}
}
This worked as long as SomeProp was null. As soon as a value was given to SomeProp the same error showed up. I went to the next level and decorated CustomBaseProp as well and that worked. However, I stopped there because there are more custom classes in CustomBaseProp and wanted to seek alternative solutions. To fix completely fix it, you would have to decorate the CustomBaseProp class and other custom classes with the appropriate attributes.
I have a class with some properties with DisplayNameEx attribute, which is derived from DisplayNameAttibute:
public class Settings
{
[DisplayNameEx("User")]
public UserData User { get; set; }
}
public class DisplayNameExAttribute : DisplayNameAttribute
{
public DisplayNameExAttribute(string id)
{
}
}
I pass as string id ALLWAYS name of my property, so it would be easier writing code this way:
public class Settings
{
[DisplayNameEx()]
public UserData User { get; set; }
}
Property name I can get here with CallerMemberName attribute:
public class DisplayNameExAttribute : DisplayNameAttribute
{
public DisplayNameExAttribute([CallerMemberName] string propertyName = null)
{
//propertyName I get, what about class name (Settings in my case)?
}
}
Is it possible to get class name also?
It's impossible. Best way in your case is parameter with class name in the constructor of the attribute:
public class DisplayNameExAttribute : DisplayNameAttribute
{
public DisplayNameExAttribute(string className = null)
{
}
}
and then use it like this:
public class Settings
{
[DisplayNameEx("Settings")]
public UserData User { get; set; }
}
I used a similar technique before .NET 4's localizable DisplayAttribute. From .NET 4 I use the DisplayAttribute because it supports localization and it's much more powerful.
I personally would not recommend your idea because of mainly two reasons.
Potential refactoring issues. If one renames your property, he doesn't have any idea that he should also rename the resource with the same name. Same stands for class names. After a few months even you can forget it time to time.
The code is not straightforward. Other developers could not easily understand what your attributes are doing, and even hard to see at first glance that it's related to localization.
I think the best is just to put there those strings.
I want to specify that one property in an XML serializable class is an attribute of another property in the class, not of the class itself. Is this possible without creating additional classes?
For example, if I have the following C# class
class Alerts
{
[XmlElement("AlertOne")]
public int AlertOneParameter { get; set; }
public bool IsAlertOneEnabled { get; set; }
}
how can I specify that IsAlertOneEnabled is an attribute of AlertOne so that the XML serializes to the following?
<Alerts>
<AlertOne Enabled="True">99</AlertOne>
</Alerts>
If you are using XmlSerializer with default (non-IXmlSerializable) serialization, then indeed: this cannot be achieved without adding an extra class that is the AlertOne, with an attribute and a [XmlText] value.
If you implement IXmlSerializable it should be possible, but that is not a nice interface to implement robustly (the deserialization, in particular, is hard; if it is write-only then this should be fine). Personally I'd recommend mapping to a DTO model with the aforementioned extra class.
Other tools like LINQ-to-XML would make it pretty simple, of course, but work differently.
An example of a suitable DTO layout:
public class Alerts
{
[XmlElement("AlertOne")]
public Alert AlertOne { get; set; }
}
public class Alert
{
[XmlText]
public int Parameter { get; set; }
[XmlAttribute("Enabled")]
public bool Enabled { get; set; }
}
You could of course add a few [XmlIgnore] pass-thru members that talk to the inner instance.