Can I make ServiceStack Deserialize json value of 1 as true?
Here's a unit test showing what I want to do.
Is this possible? if so how?
public class Foo
{
public bool isWorking { get; set; }
}
...
[Test]
public void Deserialise1AsBoolean()
{
var json = #"{""isWorking"": 1}";
var myFoo = json.FromJson<Foo>();
Assert.IsTrue(myFoo.isWorking);
}
This is now built into ServiceStack.Text with this commit available from v3.9.55+.
EDIT Here's my solution, but please check out Mythz as well since I'm sure that will work also.
I deserialise to a custom struct MyBool rather than bool.
Here's the code for the MyBool struct.
public struct MyBool
{
public bool Value { get; set; }
public static MyBool Parse(string value)
{
return new MyBool {Value = (value == "1" || value=="true")};
}
public override string ToString()
{
return Value.ToString(CultureInfo.InvariantCulture);
}
public static implicit operator bool(MyBool lValue)
{
return lValue.Value;
}
and change Foo to:
public class Foo
{
public MyBool isWorking { get; set; }
}
Criticisms welcome.
Related
Still pretty new to maui and mvvm, I'm trying to figure out of to access elements in a observable property class in a method. For example I have a login class like below:
public class LoginModel
{
public string Failmessage { get; set; } = string.Empty;
public string Username { get; set; } = string.Empty;
public string Password { get; set; } = string.Empty;
public bool Validusername { get; set; } = true;
public bool Vaildpassword { get; set; } = true;
}
In my modelview I call the observable property LoginModel loginModel
[ObservableProperty]
LoginModel LoginModel
public LoginPageViewModel()
{
LoginModel login= new();
}
I want to check the property value in a method like below but it keeps giving me errors. like Fields with [ObservableProperty] should not be directly referenced, and the generated properties should be used instead.
if ((string.IsNullOrEmpty(LoginModel.Username)) || (string.IsNullOrWhiteSpace(LoginModel.Username)))
{
IsBusy = false;
LoginModel.Validusername = false;
return;
}
Am I doing something wrong here?
I want to be able to access the element of the class in a method.
From your code, I found that you should have used nuget CommunityToolkit.Mvvm.
And the problem is that you didn't use CommunityToolkit.Mvvm correctly.
You can try to add prefix partial before class LoginPageViewModel and declare loginmodel starting with small letter,thenLoginmodel property will be generated for you by MVVM Toolkit.
You can refer to the following code:
public partial class LoginPageViewModel:ObservableObject
{
[ObservableProperty]
LoginModel loginmodel;
public LoginPageViewModel() {
//LoginModel login = new();
Loginmodel = new LoginModel();
}
public void Test() {
if ((string.IsNullOrEmpty(Loginmodel.Username)) || (string.IsNullOrWhiteSpace(Loginmodel.Username)))
{
//IsBusy = false;
Loginmodel.Validusername = false;
return;
}
}
}
Update
now the if statement is working but when I set
Loginmodel.Validusername = false; the view is not updating with this
new value.
If you want the UI update automatically once changing the value of model Loginmodel, you also need to implement interface INotifyPropertyChanged for class LoginModel.
Please refer to the following code:
public class LoginModel: INotifyPropertyChanged
{
public string Failmessage { get; set; } = string.Empty;
public string Username { get; set; } = string.Empty;
public string Password { get; set; } = string.Empty;
// public bool Validusername { get; set; } = true;
// modify code as follows
private bool _validusername;
public bool Validusername
{
get => _validusername;
set
{
SetProperty(ref _validusername, value);
}
}
public bool Vaildpassword { get; set; } = true;
bool SetProperty<T>(ref T storage, T value, [CallerMemberName] string propertyName = null)
{
if (Object.Equals(storage, value))
return false;
storage = value;
OnPropertyChanged(propertyName);
return true;
}
protected void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
public event PropertyChangedEventHandler PropertyChanged;
}
ViewModels are ObservableObjects, not ObservableProperties.
They contain ObservableProperties.
Do not construct manually your ViewModels. Use dependency injection and register them as services.
For this what you call "check property value", that are actually ObservableProperties in ObservableObject, there is ObservableValidator class.
https://learn.microsoft.com/en-us/dotnet/communitytoolkit/mvvm/observablevalidator
Fix this, and ask if you have any questions.
I am trying to get the value of a "NotMapped" property for a Entity/class when intercepting a DbUpdateCommandTree.
I have looked through the various metadata, but I cannot find the "link" to the Entity from the CommandTree, so unfortunately I am stuck.
Is it even possible ?
public class SomeEntity
{
public int ID { get; set; }
[NotMapped]
public int SomeUnmappedProperty { get; set; }
}
public class CommandTreeInterceptor : IDbCommandTreeInterceptor
{
public void TreeCreated(DbCommandTreeInterceptionContext ctx)
{
if (ctx.OriginalResult.DataSpace == DataSpace.SSpace)
{
var updateCommand = ctx.OriginalResult as DbUpdateCommandTree;
if (updateCommand != null)
{
// I would like to get a value of a specific property here.
// Pseudo code
var val = updateCommand.Entity.GetPropertyValue("SomeUnmappedProperty") as int;
}
}
}
}
My problem is that my JsonConverter doesn't seem to get invoked by the json.net de-serialization process when the converter is applied to an implementation of an interface, and the propertytype is the interface.
I use TypeNameHandling = TypeNameHandling.Objects to add $type to the json. I do so both on serialization and on de-serialization.
And when I have a property that is of an implementation of the interface the class' converter is invoked properly.
But when I have a property of interface type, the concrete class' converter is not invoked.
When I deserialize this class my JsonDataBagCreationConverter will be invoked by the RealTelephone but not by the Telephone because this is an interface.
Even though they are both serialized with the correct $type.
This results in RealTelephone having its .Data filled whereas Telephones .Data is null.
[JsonConverter(typeof(JsonDataBagCreationConverter<ContainerForITelephone>))]
public class ContainerForITelephone : IDataBag
{
private object _data;
private DataBagTypeEnum _dataBagTypeEnum;
public ITelephone Telephone { get; set; }
public Telephone RealTelephone { get; set; }
public object Data
{
get { return _data; }
set { _data = value; }
}
public DataBagTypeEnum DataBagType_Enum
{
get { return _dataBagTypeEnum; }
}
}
This jsonconverter is not invoked for the Telephone property. But it is for RealTelephone.
public class JsonDataBagCreationConverter<T> : JsonConverter where T : IDataBag, new()
{
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
if (reader.TokenType != JsonToken.Null)
{
var jsonObject = JObject.Load(reader);
var target = Create(objectType, jsonObject);
serializer.Populate(jsonObject.CreateReader(), target);
((IDataBag)target).Data = jsonObject.ToString();
return target;
}
return null;
}
}
[JsonConverter(typeof(JsonDataBagCreationConverter<Telephone>))]
public class Telephone : ITelephone
{
public string Name { get; set; }
public string AreaCode { get; set; }
public string Number { get; set; }
public SubPhone SubPhone { get; set; }
public object Data { get; set; }
public DataBagTypeEnum DataBagType_Enum { get; set; }
}
I look forward to hearing from you, thanks
Jan
SOLVED:
public class JsonDataBagCreationConverter<T> : JsonConverter where T:IDataBag
{
//, new() prevented us from using interfaces. Activator.CreateInstance did the trick in Create
//Used when the object decorated with [JsonConverter(typeof(JsonDataBagCreationConverter<xxxx>))] is de-serialized
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
var jsonObject = JObject.Load(reader);
if (objectType.IsInterface)
{
// Interfaces cannot be instantiated but must be converted to their "real" implemented type
// Because we serialize with settings.TypeNameHandling = TypeNameHandling.Objects;
// A $type property is added to the json by the deserializer.
string type = jsonObject["$type"].ToString();
var typesAsArray = type.Split(',');
var wrappedTarget = Activator.CreateInstance(typesAsArray[1], typesAsArray[0]);
var realTarget = wrappedTarget.Unwrap() as IDataBag;
serializer.Populate(jsonObject.CreateReader(), realTarget); // Will call this function recursively for any objects that have JsonDataBagCreationConverter as attribute
((IDataBag)realTarget).Data = jsonObject.ToString(); // This is where custom data is stored in databag
return realTarget;
}
// Non interface
var target = Create(objectType, jsonObject);
serializer.Populate(jsonObject.CreateReader(), target); // Will call this function recursively for any objects that have JsonDataBagCreationConverter as attribute
((IDataBag)target).Data = jsonObject.ToString(); // This is where custom data is stored in databag
return target;
}
public override bool CanRead
{
get
{
return true;
}
}
public override bool CanWrite
{
get
{
return false;
}
}
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
throw new Exception("WriteJson not implemented");
}
protected IDataBag Create(Type objectType, JObject jsonObject)
{
var aa = Activator.CreateInstance(objectType);
return aa as IDataBag;
// return new T(); // this demands ,new() on the class and then it will not work with interfaces
}
public override bool CanConvert(Type objectType)
{
return typeof(T).IsAssignableFrom(objectType);
}
}
I have an auto generated class from an xml like the following:
public partial class XmlClass {
private decimal num1;
private ClassA[] classField;
/// <remarks/>
public decimal num1 {
get;
set;
}
}
/// <remarks/>
[System.Xml.Serialization.XmlElementAttribute("classA")]
public ClassA[] classA {
get{...};
set{...};
}
}
public partial class ClassA {
private object[] itemsField;
private string typeField;
[System.Xml.Serialization.XmlElementAttribute("commands", typeof(classACommands))]
[System.Xml.Serialization.XmlElementAttribute("minVersion", typeof(string))]
public object[] Items {
get {
return this.itemsField;
}
set {
this.itemsField = value;
}
}
[System.Xml.Serialization.XmlAttributeAttribute()]
public string type {
get {
return this.typeField;
}
set {
this.typeField = value;
}
}
}
ClassA has the string and commands class as Objects in the Object[]. I can see everything is deserialized perfectly and get them by: (commands)myXmlClass.classA.ElementAt(i).Items[3], where i is from the index of the ClassA array. But how can I get or set them without using '3'? It might be different in different ClassA elements.
Not sure I totally understand your question, but you can use a foreach loop:
foreach(ClassA a in myXmlClass.classA) {
Console.WriteLine(a.num1.ToString());
}
I need to access an enum through a webservice.
As a webservice allocates 0 based integers to an enumeration (ignoring preset values in enum definition), I built the following:
public class StatusType
{
public StatusVal Pending { get { return new StatusVal( 1, "Pending"); } }
public StatusVal Authorised { get { return new StatusVal(2, "Authorised"); } }
public StatusVal Rejected { get { return new StatusVal(3, "Rejected"); } }
public StatusVal Sent { get { return new StatusVal(4, "Sent"); } }
public StatusVal InActive { get { return new StatusVal(5, "InActive"); } }
public List<StatusVal> StatusList()
{
List<StatusVal> returnVal = new List<StatusVal>();
StatusType sv = new StatusType();
returnVal.Add(sv.Pending);
returnVal.Add(sv.Authorised);
returnVal.Add(sv.Rejected);
returnVal.Add(sv.Sent);
returnVal.Add(sv.InActive);
return returnVal;
}
}
public class StatusVal
{
public StatusVal(int a, string b)
{
this.ID = a;
this.Name = b;
}
public int ID { get; set; }
public string Name { get; set; }
}
I then get the list of StatusVal with the following webmethod:
[WebMethod]
public List<ATBusiness.StatusVal> GetStatus()
{
ATBusiness.StatusType a = new ATBusiness.StatusType();
return a.StatusList();
}
I cannot however use this webmethod as referring it, I get the error: StatusVal cannot be serialized because it does not have a parameterless constructor.
I don't quite understand: should I pass params into the StatusValue type defined as the WebMethod's return Type?
I need this to return a list of StatusVals as per the StatusList() method.
As the error says, your class needs a constructor without parameters. When unserializing, the runtime will use that constructor instead of the one you have defined.
Something like:
public StatusVal()
{
}
When you created a constructor with parameters, you are automatically removing the default no-parameter constructor, and that's what the compiler is complaining about.