I don't know why i can't find an easy quick lookup for this on the web but i was wondering what the relationship is between Deserializing an XML representation of an object and the constructor for that object?
I am assuming it uses the Default Constructor. And if that's the case, it will run the code in the constructor, but not update the object itself after that to reflect the XML?
Here's a bit more context on what i mean...
I have an object with two properties that are actually objects as well:
public class Deployment
{
public AppPoolSettings AppPool { get; set; }
public WebSiteSettings Site { get; set; }
public Deployment()
{
//the object constructors below init their internal properties as well...
this.AppPool = new AppPoolSettings();
this.Site = new WebSiteSettings();
}
}
The problem I am currently having is that in the XML, the AppPool property can be null (say, if you're deploying an HTML only package). The serialization procedure works properly, that is to say, the resulting XML only contains an entry for Site, and no entry for AppPool.
However, when I deserialize that XML, the AppPool property of my Deployment object is always instantiated and initialized... which is not what the XML is saying.
Am I doing something wrong or is it really just because of the default constructor?
See I would've expected the deserializer to perform the tasks in this order:
1- Call default constructor
2- Does AppPool property exist in XML?
Yes --> Fill,
No --> Set to NULL
3- Does the Site property exist in XML?
Yes --> Fill,
No --> Set to NULL
Why is it not doing that?
I believe the correct answer is: yes, counterintuitive handling of null properties (omitting them in serialized data and doing nothing with them on deserialization) is a feature of XmlSerializer. But you can override that behavior and force XmlSerializer to write nulls to XML with attributes like that:
public class Deployment
{
[XmlElement(IsNullable = true)]
public AppPoolSettings AppPool { get; set; }
[XmlElement(IsNullable = true)]
public WebSiteSettings Site { get; set; }
public Deployment()
{
//the object constructors below init their internal properties as well...
this.AppPool = new AppPoolSettings();
this.Site = new WebSiteSettings();
}
}
then you'll get <AppPool xsi:nil="true" /> in XML and expected deserialization.
The default constructor is called, and therefore, Site and AppPool are assigned. If you want them be null maybe you could try this code:
public class Deployment
{
private AppPoolSettings appPool;
public AppPoolSettings AppPool
{
get { return appPool; }
set
{
// if (appPool == null)
// appPool = new AppPoolSettings();
appPool = value;
}
}
private WebSiteSettings site;
public WebSiteSettings Site
{
get { return site; }
set
{
// if (site == null)
// site = new WebSiteSettings();
site = value;
}
}
public Deployment()
{
// No instatiation anymore...
}
}
You expectations are wrong. XmlSerializer will construct object (by calling parameterless constructor, if there is none - exception is thrown). Then properties will be populated one by one using some reflection magic.
So what happens:
Constructor is called, in which you set values of AppPool and Site.
There is Site property in xml, it's deserialized and assigned.
But there is no AppPool in xml, so nothing changes and its value stay (not null).
To have null for AppPool you should not set its value in constructor. Then it will stay such if missing in xml.
Here is one possible solution:
public class Deployment
{
public AppPoolSettings AppPool { get; set; }
public WebSiteSettings Site { get; set; }
// used by deserializer
public Deployment() { }
// use this to construct object
public static Deployment Create()
{
return new Deployment()
{
AppPool = new AppPoolSettings(),
Site = new WebSiteSettings()
};
}
}
Related
I am writing a proxy to wrap a WCF service with ASP.net Core and this is my first time using ASP.Net core.
I am using an auto-generated WCF service contract, and the issue is my WCF service changes frequently, so I must update/refresh the WCF service, and whenever I update my contract, I lose my minor tweaks.
I'm only trying to:
Hide some properties - via changing public string Property to internal string Property. I've tried [IgnoreDataMember] and [JsonIgnore] but those don't seem to work
Make some properties required - via RequiredAttribute
Default some property values - via DefaultValueAttribute
I've tried two approaches so far but they're not working fully.
This represents the automatically generated WCF Contract where I want to require & default MyProperty1 and hide MyProperty2:
// This is the automatically generated WCF Contract
public partial class MyClass
{
private string myPropertyField1;
private string myPropertyField2;
public string MyProperty1
{
get { return this.myPropertyField1; }
set { this.myPropertyField1 = value; }
}
public string MyProperty2
{
get { return this.myPropertyField2; }
set { this.myPropertyField2 = value; }
}
}
Method 1:
ModelMetadataType to override the contract metdata. This partially works, but not for all attributes for some reason? It feels like a bug.
[ModelMetadataType(typeof(MyClassMetadata))]
public partial class MyClass { }
public partial class MyClassMetadata
{
[Required] // This does not work
[DefaultValue("SomeValue")] // This DOES work?
public string MyProperty1 { get; set; } // I want this required & defaulted
[IgnoreDataMember] // This does not work to hide
[JsonIgnore] // This does not work to hide
public string MyProperty2 { get; set; } // I want this hidden from Swagger view
}
Which works to default values, but it doesn't appear to make it required or hidden?
Method 2:
I tried creating derived class, and then using the derived class instead of MyClass for the controller api arguments, but then when I try to call it I receive an error that says something like Type MyClassDerived was not expected. Use XmlInclude to specify unexpected types...
public partial class MyClassDerived : MyClass
{
[Required] // This puts "*" next to it in Swagger
[DefaultValue("SomeValue")] // This defaults the value in Swagger
public new string MyProperty1
{
get { return base.MyProperty1; }
set { base.MyProperty1 = value; }
}
internal new string MyProperty2 // This works to hide from Swagger
{
get { return base.MyProperty2; }
set { base.MyProperty2 = value; }
}
}
How can I hide/default/require contract properties without directly modifying an auto-generated WCF contract service class?
The issue is with the following class:
[DebuggerDisplay("{Kind}: {Identifier}")]
public class SocialConnection
{
public virtual Guid UniqueId
{
get { return Id; }
set { Id = value; }
}
// SocialConnectionKind is an enumeration
public virtual SocialConnectionKind Kind { get; set; }
public virtual string Identifier { get; set; }
}
Kind property never gets serialized: when I request an object which has an associated SocialConnection I never get the whole property.
BTW, if I manually call JsonConvert.SerializeObject it gets serialized. It should be something with the default media-type formatter but I can't figure out the solution so far.
The issue which was causing this serialization problem was very simple. Check SocialConnectionKind enumeration:
public enum SocialConnectionKind
{
Skype,
Facebook,
Twitter,
LinkedIn,
Hangouts
}
Did you already notice what could be the problem? The issue wouldn't be reproduced if the value would be any excepting Skype!
Why? Enumerations start with 0 and see how I've configured my WebAPI's HttpConfiguration:
config.Formatters.JsonFormatter.SerializerSettings.DefaultValueHandling =
DefaultValueHandling.Ignore;
Which is the default value of the Enum default underlying type int? Yes, it's 0.
So, what solved the issue?
public enum SocialConnectionKind
{
Skype = 1, // <--- The issue was solved starting the enumeration from 1!
Facebook,
Twitter,
LinkedIn,
Hangouts
}
Another approach
As #Avner Shahar-Kashtan have pointed out in some comment, I could also solve this issue using [JsonProperty(DefaultValueHandling = DefaultValueHandling.Include)] attribute:
[DebuggerDisplay("{Kind}: {Identifier}")]
public class SocialConnection
{
public virtual Guid UniqueId
{
get { return Id; }
set { Id = value; }
}
// SocialConnectionKind is an enumeration
[JsonProperty(DefaultValueHandling = DefaultValueHandling.Include)]
public virtual SocialConnectionKind Kind { get; set; }
public virtual string Identifier { get; set; }
}
...and this way there's no need of starting an enumeration from 1.
Anyway, in my particular case, I prefer to stay with the start from 1 approach, because I find cleaner avoid polluting my POCOs with serialization-specific attributes because SocialConnection class lives in a shared library and this serialization issue is an implementation issue in a concrete project.
I have a ViewModel that I can decorate with the [Required] attribute (see below). I've come to the point where I need to let the client control which fields are required or not. They can configure this trough XML and all this info is stored in the Model when it's first created. Now I have fields that are not decorated with [Required] but still need to get validated (as per "user settings") before submitting (for example the Phone field).
public class MyBusinessObjectViewModel
{
[Required]
public string Email { get; set; } //compulsory
public string Phone { get; set; } //not (yet) compulsory, but might become
}
If the user will not enter the Phone number, the data will still get posted. Wanting not to mess with custom validators, I just add the "data-val" and "data-val-required" attributes to the Html, like this:
Dictionary<string, object> dict = new Dictionary<string, object>();
dict.Add("data-val", "true");
dict.Add("data-val-required", "This field is required.");
#Html.TextBoxFor(x => x, dict);
This forces the client side validation for all the properties that are dynamically set as required. Is this good practice? What kind of side effects can I expect?
You should look into extending the meta model framework with your own metadata provider to do the actual binding between your site's configuration and the model metadata. You can actually set the required property flag to true on the property model metadata during the metadata creation process. I can't remember for sure whether this causes the built in editor templates to generate the attribute, but I think it does. Worst case scenario you can actually create and attach a new RequiredAttribute to the property, which is a tad bit kluggy, but works pretty well in certain scenarios.
You could also do this with IMetadataAware attributes, especially if Required is the only metadata aspect your users can customize, but the implementation really depends on what you're trying to do.
One major advantage of using a custom ModelMetadataProvider in your specific case is that you can use dependency injection (via ModelMetadataProviders) to get your customer settings persistence mechanism into scope, whereas with the data attribute you only get to write an isolated method that runs immediately after the metadata model is created.
Here is a sample implementation of a custom model metadata provider, you'd just have to change the client settings to whatever you wanted to use.
UPDATED but not tested at all
public class ClientSettingsProvider
{
public ClientSettingsProvider(/* db info */) { /* init */ }
public bool IsPropertyRequired(string propertyIdentifier)
{
// check the property identifier here and return status
}
}
public ClientRequiredAttribute : Attribute
{
string _identifier;
public string Identifier { get { return _identifer; } }
public ClientRequiredAttribute(string identifier)
{ _identifier = identifier; }
}
public class RequiredModelMetadataProvider : DataAnnotationsModelMetadataProvider
{
ClientSettings _clientSettings;
public RequiredModelMetadataProvider(ClientSettings clientSettings)
{
_clientSettings = clientSettings;
}
protected override ModelMetadata CreateMetadata(IEnumerable<Attribute> attributes, Type containerType, Func<object> modelAccessor, Type modelType, string propertyName)
{
// alternatively here is where you could 'inject' a RequiredAttribute into the attributes list
var clientRequiredAttribute = attributes.OfType<ClientRequiredAttribute>().SingleOrDefault();
if(clientRequiredAttribute != null && _clientSettings.IsPropertyRequired(clientRequiredAttribute.Identifier))
{
// By injecting the Required attribute here it will seem to
// the base provider we are extending as if the property was
// marked with [Required]. Your data validation attributes should
// be added, provide you are using the default editor templates in
// you view.
attributes = attributes.Union(new [] { new RequiredAttribute() });
}
var metadata = base.CreateMetadata(attributes, containerType, modelAccessor, modelType, propertyName);
// REMOVED, this is another way but I'm not 100% sure it will add your attributes
// Use whatever attributes you need here as parameters...
//if (_clientSettings.IsPropertyRequired(containerType, propertyName))
//{
// metadata.IsRequired = true;
//}
return metadata;
}
}
USAGE
public class MyModel
{
[ClientRequired("CompanyName")]
public string Company { get; set; }
}
public class MyOtherModel
{
[ClientRequired("CompanyName")]
public string Name { get; set; }
public string Address { get; set; }
}
Both of these models would validate the string "CompanyName" against your client settings provider.
Not wanting to mess with custom validators, so you messed in the View obfuscating the logic of your validation by removing it from the place where it is expected to be found.
Really, don't be afraid of creating a custom attribute validator. What you are doing right now is getting a technical debt.
I'm working on creating a session for a user login on my website. I can initialize the session and use its members just fine, but I also need a method within my session class that will store itself. I need to provide HttpSessionState as an input parameter, and then store it into an object like: Session["sessionName"]=this;.
Furthermore, when I want to retrieve the session, it won't yet be created, so it must be static. Then I need to return a new instance of my session class with the properties filled (username and companyID) out of the HttpSessionState.
How can this be done in my session class? What I've described above is from the research I've done that provides a particular solution to my problem, but since I'm new to using session, I don't quite understand it.
Thanks.
Snippet of my session class:
public class MySession : System.Web.UI.Page
{
private MySession()
{
Username = Business.User.labelUsername;
CompanyId = Business.User.labelCompanyId;
}
public static MySession Current
{
get
{
try
{
MySession session = (MySession)HttpContext.Current.Session["sessionName"];
if (session == null)
{
session = new MySession();
HttpContext.Current.Session["sessionName"]=session;
}
return session;
}
catch (NullReferenceException e)
{
Debug.WriteLine("NullReferenceException:");
Debug.WriteLine(e);
}
return null;
}
}
public string Username
{
get; set;
}
public string CompanyId
{
get; set;
}
}
You could try using a serialized "session info" object:
[Serializable]
public class SessionInfo
{
// Stuff to store in session
public string Name { get; set; }
public int Foo { get; set; }
private SessionInfo()
{
// Constructor, set any defaults here
Name = ""
Foo = 10;
}
public static SessionInfo Current
{
get
{
// Try get session info from session
var info = HttpContext.Current.Session["SessionInfo"] as SessionInfo;
// Not found in session, so create and store new session info
if (info == null)
{
info = new SessionInfo();
HttpContext.Current.Session["SessionInfo"] = info;
}
return info;
}
}
}
You can then use this from within your application like this:
SessionInfo.Current.Name = "Something Here";
SessionInfo.Current.Foo = 100;
The serialization/deserialization is all done within the SessionInfo object, and you get the benefit of type safe data.
What you are asking about is called serialization and deserialization.
Serialization is taking an object and converting it to a format, such as a string, that can be stored. Deserialization is the reverse of that action.
The "quick" way is to add the [Serializable] attribute to your class. However, without knowing the details of that class it's hard to say whether it is in fact easily serializable without a bit of work.
Here's a walkthrough: http://msdn.microsoft.com/en-us/library/vstudio/et91as27.aspx
I have this object:
public class Announcement
{
public int Id { get; set; }
public DateTime DateSent { get; set; }
private IList<string> _recipients;
public IList<string> Recipients
{
get { return _recipients; }
set { _recipients = value; }
}
public string RecipientsString
{
get { return String.Join("\n", _recipients); }
set { _recipients = value.Split('\n').ToList(); }
}
}
I can populate this object with the DateSent and RecipientString (a string of email addresses separated by \n) and save it to the database with no problems.
Now I want to move this to a web service so we can use it across multiple apps.
I created the exact same object in the webservice, and testing locally (on the service) everything works as expected.
But if I populate the object on the client and pass it to the service to be saved, the RecipientString is always empty (not null). The DateSent is fine.
I'm guessing the data is getting lost in serialization, but I don't know why, or how to solve this. I thought also, it could have something to do with the # in the email address, but I've ruled that out. Any suggestions?
This happens because de WSDL that is generated to describe your service can't describe the function that is used in your get and set functions. I suggest you keep RecipientsString as a common property, and create a private method GetRecipients on your class that processes the RecipientsString value and returns the list you need.
Use RecipientsString without backing field.