I am trying to serialize and deserialize FieldInfo and PropertyInfos, and I am getting this error, which I dont understand, nor do I know how to fix or change what I am doing to fix this: Newtonsoft.Json.JsonSerializationException: Could not create an instance of type System.Reflection.FieldInfo. Type is an interface or abstract class and cannot be instantiated.
The basis of what I am trying to do is serialize and deserialize a class called AutoLType that looks like this:
public class AutoLType
{
public static implicit operator AutoSType(AutoLType l)
{
if (l.PropertyOrField == PropOrField.Field)
{
return new AutoSType(l.Field, l.Attribute, AutoSType.PropOrField.Field);
}
else
{
return new AutoSType(l.Property, l.Attribute, AutoSType.PropOrField.Prop);
}
}
public enum PropOrField
{
Prop,
Field
}
public PropertyInfo Property { get; set; }
public FieldInfo Field { get; set; }
public AutoLoad Attribute { get; set; }
public PropOrField PropertyOrField { get; set; }
public AutoLType(object pOrF, AutoLoad attr, PropOrField propOrField)
{
if (pOrF is PropertyInfo)
{
Property = (PropertyInfo)pOrF;
}
else if(pOrF is FieldInfo)
{
Field = (FieldInfo)pOrF;
}
Attribute = attr;
PropertyOrField = propOrField;
}
}
public AutoLType Load(AutoLType autoLTypeToLoad)
{
AutoLType a = null;
try
{
string filePath = Application.persistentDataPath + autoLTypeToLoad.Attribute.FilePath;
string json = File.ReadAllText(filePath);
a = JsonConvert.DeserializeObject<AutoLType>(json);
}
catch (Exception ex)
{
Debug.Log(ex.ToString());
}
return a;
}
Let me know if you need to see the AutoLoad class, but it isnt giving any problems right now.
Related
Some of my actions accept models like:
public class PaymentRequest
{
public decimal Amount { get; set; }
public bool? SaveCard { get; set; }
public int? SmsCode { get; set; }
public BankCardDetails Card { get; set; }
}
public class BankCardDetails
{
public string Number { get; set; }
public string HolderName { get; set; }
public string ExpiryDate { get; set; }
public string ValidationCode { get; set; }
}
And the action method looks like:
[HttpPost]
[Route("api/v1/payment/pay")]
public Task<BankCardActionResponse> Pay([FromBody] PaymentRequest request)
{
if (request == null)
throw new HttpResponseException(HttpStatusCode.BadRequest);
return _paymentService.PayAsync(DataUserHelper.PhoneNumber, request);
}
I use Nlog. I think it's clear this is a bad idea to log all this bank data. My log config file contained the following line:
<attribute name="user-requestBody" layout="${aspnet-request-posted-body}"/>
I logged the request. I decided to refactor that and planned the following strategy. Actions that contain sensitive data into their requests I will mark with an attribute like
[RequestMethodFormatter(typeof(PaymentRequest))]
then take a look at my custom renderer:
[LayoutRenderer("http-request")]
public class NLogHttpRequestLayoutRenderer : AspNetRequestPostedBody
{
protected override void DoAppend(StringBuilder builder, LogEventInfo logEvent)
{
base.DoAppend(builder, logEvent);
var body = builder.ToString();
// Get attribute of the called action.
var type = ... // How can I get "PaymentRequest" from the [RequestMethodFormatter(typeof(PaymentRequest))]
var res = MaskHelper.GetMaskedJsonString(body, type);
// ... and so on
}
}
I think you understand the idea. I need the type from the method's RequestMethodFormatter attribute. Is it even possible to get it into the renderer? I need it because I'm going to deserialize request JSON into particular models (it's gonna be into the MaskHelper.GetMaskedJsonString), work with the models masking the data, serialize it back into JSON.
So, did I choose a wrong approach? Or it's possible to get the type from the attribute into the renderer?
After some research, I ended up with the following solution:
namespace ConsoleApp7
{
internal class Program
{
private static void Main()
{
var sourceJson = GetSourceJson();
var userInfo = JsonConvert.DeserializeObject(sourceJson, typeof(User));
Console.WriteLine("----- Serialize without Resolver-----");
Console.WriteLine(JsonConvert.SerializeObject(userInfo));
Console.WriteLine("----- Serialize with Resolver-----");
Console.WriteLine(JsonConvert.SerializeObject(userInfo, new JsonSerializerSettings
{
ContractResolver = new MaskPropertyResolver()
}));
}
private static string GetSourceJson()
{
var guid = Guid.Parse("3e92f0c4-55dc-474b-ae21-8b3dac1a0942");
return JsonConvert.SerializeObject(new User
{
UserId = guid,
Age = 19,
Name = "John",
BirthDate = new DateTime(1990, 5, 12),
Hobbies = new[]
{
new Hobby
{
Name = "Football",
Rating = 5,
DurationYears = 3,
},
new Hobby
{
Name = "Basketball",
Rating = 7,
DurationYears = 4,
}
}
});
}
}
public class User
{
[MaskGuidValue]
public Guid UserId { get; set; }
[MaskStringValue("***")] public string Name { get; set; }
public int Age { get; set; }
[MaskDateTimeValue]
public DateTime BirthDate { get; set; }
public Hobby[] Hobbies { get; set; }
}
public class Hobby
{
[MaskStringValue("----")]
public string Name { get; set; }
[MaskIntValue(replacement: 11111)]
public int Rating { get; set; }
public int DurationYears { get; set; }
}
public class MaskPropertyResolver : DefaultContractResolver
{
protected override IList<JsonProperty> CreateProperties(Type type, MemberSerialization memberSerialization)
{
var props = base.CreateProperties(type, memberSerialization);
var allowedPropertyTypes = new Type[]
{
typeof(Guid),
typeof(DateTime),
typeof(string),
typeof(int),
};
foreach (var prop in props.Where(p => allowedPropertyTypes.Contains(p.PropertyType)))
{
if (prop.UnderlyingName == null)
continue;
var propertyInfo = type.GetProperty(prop.UnderlyingName);
var attribute =
propertyInfo?.GetCustomAttributes().FirstOrDefault(x => x is IMaskAttribute) as IMaskAttribute;
if (attribute == null)
{
continue;
}
if (attribute.Type != propertyInfo.PropertyType)
{
// Log this case, cause somebody used wrong attribute
continue;
}
prop.ValueProvider = new MaskValueProvider(propertyInfo, attribute.Replacement, attribute.Type);
}
return props;
}
private class MaskValueProvider : IValueProvider
{
private readonly PropertyInfo _targetProperty;
private readonly object _replacement;
private readonly Type _type;
public MaskValueProvider(PropertyInfo targetProperty, object replacement, Type type)
{
_targetProperty = targetProperty;
_replacement = replacement;
_type = type;
}
public object GetValue(object target)
{
return _replacement;
}
public void SetValue(object target, object value)
{
_targetProperty.SetValue(target, value);
}
}
}
[AttributeUsage(AttributeTargets.Property)]
public class MaskStringValueAttribute : Attribute, IMaskAttribute
{
public Type Type => typeof(string);
public object Replacement { get; }
public MaskStringValueAttribute(string replacement)
{
Replacement = replacement;
}
}
[AttributeUsage(AttributeTargets.Property)]
public class MaskIntValueAttribute : Attribute, IMaskAttribute
{
public object Replacement { get; }
public Type Type => typeof(int);
public MaskIntValueAttribute(int replacement)
{
Replacement = replacement;
}
}
[AttributeUsage(AttributeTargets.Property)]
public class MaskGuidValueAttribute : Attribute, IMaskAttribute
{
public Type Type => typeof(Guid);
public object Replacement => Guid.Empty;
}
[AttributeUsage(AttributeTargets.Property)]
public class MaskDateTimeValueAttribute : Attribute, IMaskAttribute
{
public Type Type => typeof(DateTime);
public object Replacement => new DateTime(1970, 1, 1);
}
public interface IMaskAttribute
{
Type Type { get; }
object Replacement { get; }
}
}
I hope somebody will find it helpful.
You can try nuget package https://www.nuget.org/packages/Slin.Masking and https://www.nuget.org/packages/Slin.Masking.NLog.
It can easily be integrated with DotNet projects with slight changes, and you can define your rules for it. But the document needs some improvement.
As a suggestion, you can use two files:
masking.json (can be a generic one, that shared across all projects)
masking.custom.json (can be used with particular rules for specific projects)
Iam implemented a webservice using c# webapi,but my json response array is empty.
My code
public object Post([FromBody] castdet castdet1)
{
mid = castdet1.mid1;
return Request.CreateResponse(jsonvalues(mid));
}
private object jsonvalues(string mid)
{
DataTable dtalcast = GetAllcast();
foreach (DataRow drow in dtalcast.Rows)
{
string mouvieid = drow["MovieMasterId"].ToString();
string actname = drow["ActorName"].ToString();
string charname = drow["CharacterName"].ToString();
if (mouvieid == mid)
{
temp = 1;
castdet.Add(new myobject(actname, charname));
}
}
return castdet;
}
public class castdet
{
public string mid1 { get; set; }
}
public class myobject
{
string actorname;
string charactername;
public myobject(string v1, string v2)
{
actorname = v1;
charactername = v2;
}
}
My json response string is like this [{},{}],its empty.What went wrong for me?
You need to make the fields of myobject be public. Or better yet make them be public properties:
public class myobject
{
public string actorname { get; set; }
public string charactername { get; set; }
public myobject(string actorname, string charactername)
{
this.actorname = actorname;
this.charactername = charactername;
}
}
(You may have other problems - your code is incomplete and does not compile. Also, you should modify your methods to explicitly return the actual types being returned, not just to return object. This allows for compile-time checking for type errors.)
Sample class:
public class ClassA
{
public int Id { get; set; }
public string SomeString { get; set; }
public int? SomeInt { get; set; }
}
Default deserializer:
var myObject = JsonConvert.DeserializeObject<ClassA>(str);
Create the same object for two different inputs
{"Id":5}
or
{"Id":5,"SomeString":null,"SomeInt":null}
How can I track properties that were missing during deserialization process and preserve the same behavior? Is there a way to override some of JSON.net serializer methods (e.g. DefaultContractResolver class methods) to achive this. For example:
List<string> missingProps;
var myObject = JsonConvert.DeserializeObject<ClassA>(str, settings, missingProps);
For the first input list should contains the names of the missing properties ("SomeString", "SomeInt") and for second input it should be empty. Deserialized object remains the same.
1. JSON has a property which is missing in your class
Using property JsonSerializerSettings.MissingMemberHandling you can say whether missing properties are handled as errors.
Than you can install the Error delegate which will register errors.
This will detect if there is some "garbage" property in JSON string.
public class ClassA
{
public int Id { get; set; }
public string SomeString { get; set; }
}
internal class Program
{
private static void Main(string[] args)
{
const string str = "{'Id':5, 'FooBar': 42 }";
var myObject = JsonConvert.DeserializeObject<ClassA>(str
, new JsonSerializerSettings
{
Error = OnError,
MissingMemberHandling = MissingMemberHandling.Error
});
Console.ReadKey();
}
private static void OnError(object sender, ErrorEventArgs args)
{
Console.WriteLine(args.ErrorContext.Error.Message);
args.ErrorContext.Handled = true;
}
}
2. Your class has a property which is missing in JSON
Option 1:
Make it a required property:
public class ClassB
{
public int Id { get; set; }
[JsonProperty(Required = Required.Always)]
public string SomeString { get; set; }
}
Option 2:
Use some "special" value as a default value and check afterwards.
public class ClassB
{
public int Id { get; set; }
[DefaultValue("NOTSET")]
public string SomeString { get; set; }
public int? SomeInt { get; set; }
}
internal class Program
{
private static void Main(string[] args)
{
const string str = "{ 'Id':5 }";
var myObject = JsonConvert.DeserializeObject<ClassB>(str
, new JsonSerializerSettings
{
DefaultValueHandling = DefaultValueHandling.Populate
});
if (myObject.SomeString == "NOTSET")
{
Console.WriteLine("no value provided for property SomeString");
}
Console.ReadKey();
}
}
Option 3:
Another good idea would be to encapsulate this check iside the class istself. Create a Verify() method as shown below and call it after deserialization.
public class ClassC
{
public int Id { get; set; }
[DefaultValue("NOTSET")]
public string SomeString { get; set; }
public int? SomeInt { get; set; }
public void Verify()
{
if (SomeInt == null ) throw new JsonSerializationException("SomeInt not set!");
if (SomeString == "NOTSET") throw new JsonSerializationException("SomeString not set!");
}
}
Another way to find null/undefined tokens during De-serialization is to write a custom JsonConverter , Here is an example of custom converter which can report both omitted tokens (e.g. "{ 'Id':5 }") and null tokens (e.g {"Id":5,"SomeString":null,"SomeInt":null})
public class NullReportConverter : JsonConverter
{
private readonly List<PropertyInfo> _nullproperties=new List<PropertyInfo>();
public bool ReportDefinedNullTokens { get; set; }
public IEnumerable<PropertyInfo> NullProperties
{
get { return _nullproperties; }
}
public void Clear()
{
_nullproperties.Clear();
}
public override bool CanConvert(Type objectType)
{
return true;
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
existingValue = existingValue ?? Activator.CreateInstance(objectType, true);
var jObject = JObject.Load(reader);
var properties =
objectType.GetProperties(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
foreach (var property in properties)
{
var jToken = jObject[property.Name];
if (jToken == null)
{
_nullproperties.Add(property);
continue;
}
var value = jToken.ToObject(property.PropertyType);
if(ReportDefinedNullTokens && value ==null)
_nullproperties.Add(property);
property.SetValue(existingValue, value, null);
}
return existingValue;
}
//NOTE: we can omit writer part if we only want to use the converter for deserializing
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
var objectType = value.GetType();
var properties =
objectType.GetProperties(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
writer.WriteStartObject();
foreach (var property in properties)
{
var propertyValue = property.GetValue(value, null);
writer.WritePropertyName(property.Name);
serializer.Serialize(writer, propertyValue);
}
writer.WriteEndObject();
}
}
Note: we can omit the Writer part if we don't need to use it for serializing objects.
Usage Example:
class Foo
{
public int Id { get; set; }
public string SomeString { get; set; }
public int? SomeInt { get; set; }
}
class Program
{
static void Main(string[] args)
{
var nullConverter=new NullReportConverter();
Console.WriteLine("Pass 1");
var obj0 = JsonConvert.DeserializeObject<Foo>("{\"Id\":5, \"Id\":5}", nullConverter);
foreach(var p in nullConverter.NullProperties)
Console.WriteLine(p);
nullConverter.Clear();
Console.WriteLine("Pass2");
var obj1 = JsonConvert.DeserializeObject<Foo>("{\"Id\":5,\"SomeString\":null,\"SomeInt\":null}" , nullConverter);
foreach (var p in nullConverter.NullProperties)
Console.WriteLine(p);
nullConverter.Clear();
nullConverter.ReportDefinedNullTokens = true;
Console.WriteLine("Pass3");
var obj2 = JsonConvert.DeserializeObject<Foo>("{\"Id\":5,\"SomeString\":null,\"SomeInt\":null}", nullConverter);
foreach (var p in nullConverter.NullProperties)
Console.WriteLine(p);
}
}
I got this problem, but defaultValue was not solution due to POCO object. I think this is simpler approach than NullReportConverter.
There are three unit tests. Root is class that encapsulate whole json. Key is type of the Property. Hope this helps someone.
using Microsoft.VisualStudio.TestTools.UnitTesting;
using Newtonsoft.Json;
namespace SomeNamespace {
[TestClass]
public class NullParseJsonTest {
[TestMethod]
public void TestMethod1()
{
string slice = "{Key:{guid:\"asdf\"}}";
var result = JsonConvert.DeserializeObject<Root>(slice);
Assert.IsTrue(result.OptionalKey.IsSet);
Assert.IsNotNull(result.OptionalKey.Value);
Assert.AreEqual("asdf", result.OptionalKey.Value.Guid);
}
[TestMethod]
public void TestMethod2()
{
string slice = "{Key:null}";
var result = JsonConvert.DeserializeObject<Root>(slice);
Assert.IsTrue(result.OptionalKey.IsSet);
Assert.IsNull(result.OptionalKey.Value);
}
[TestMethod]
public void TestMethod3()
{
string slice = "{}";
var result = JsonConvert.DeserializeObject<Root>(slice);
Assert.IsFalse(result.OptionalKey.IsSet);
Assert.IsNull(result.OptionalKey.Value);
}
}
class Root {
public Key Key {
get {
return OptionalKey.Value;
}
set {
OptionalKey.Value = value;
OptionalKey.IsSet = true; // This does the trick, it is never called by JSON.NET if attribute missing
}
}
[JsonIgnore]
public Optional<Key> OptionalKey = new Optional<Key> { IsSet = false };
};
class Key {
public string Guid { get; set; }
}
class Optional<T> {
public T Value { get; set; }
public bool IsSet { get; set; }
}
}
So I'm trying to XML serialize a List<IObject> derrived from an interface, but the IObjects are generic types... best resort to code:
public interface IOSCMethod
{
string Name { get; }
object Value { get; set; }
Type Type { get; }
}
public class OSCMethod<T> : IOSCMethod
{
public string Name { get; set; }
public T Value { get; set; }
public Type Type { get { return _type; } }
protected string _name;
protected Type _type;
public OSCMethod() { }
// Explicit implementation of IFormField.Value
object IOSCMethod.Value
{
get { return this.Value; }
set { this.Value = (T)value; }
}
}
And I have a List of IOSCMethods:
List<IOSCMethod>
of which I add objects to in the following way:
List<IOSCMethod> methodList = new List<IOSCMethod>();
methodList.Add(new OSCMethod<float>() { Name = "/1/button1", Value = 0 });
methodList.Add(new OSCMethod<float[]>() { Name = "/1/array1", Value = new float[10] });
And it's this methodList which is what I'm trying to serialize. But everytime I try, either I get a "Can't serialize an interface" but when I make it (either the IOSCMethod or the OSCMethod<T> class) implement IXmlSerializable I get the problem of "can't serialize an object with a parameterless constructor. but obviously I can't because it's an interface! lame pants.
Any thoughts?
Is this what you want:
[TestFixture]
public class SerializeOscTest
{
[Test]
public void SerializeEmpTest()
{
var oscMethods = new List<OscMethod>
{
new OscMethod<float> {Value = 0f},
new OscMethod<float[]> {Value = new float[] {10,0}}
};
string xmlString = oscMethods.GetXmlString();
}
}
public class OscMethod<T> : OscMethod
{
public T Value { get; set; }
}
[XmlInclude(typeof(OscMethod<float>)),XmlInclude(typeof(OscMethod<float[]>))]
public abstract class OscMethod
{
}
public static class Extenstion
{
public static string GetXmlString<T>(this T objectToSerialize)
{
XmlSerializer xmlSerializer = new XmlSerializer(objectToSerialize.GetType());
StringBuilder stringBuilder = new StringBuilder();
string xml;
using (var xmlTextWriter = new XmlTextWriter(new StringWriter(stringBuilder)))
{
xmlSerializer.Serialize(xmlTextWriter, objectToSerialize);
xml = stringBuilder.ToString();
}
return xml;
}
}
Ok so at first I thought this was easy enough, and maybe it is and I'm just too tired - but here's what I'm trying to do. Say I have the following objects:
public class Container
{
public string Name { get; set; }
public List<Address> Addresses { get; set; }
}
public class Address
{
public string AddressLine1 { get; set; }
public string AddressLine2 { get; set; }
public List<Telephone> Telephones { get; set; }
}
public class Telephone
{
public string CellPhone { get; set; }
}
What I need to be able to do, is 'flatten' Containers property names in to a string (including ALL child properties AND child properties of child properties) that would look something like this:
Container.Name, Container.Addresses.AddressLine1, Container.Addresses.AddressLine2, Container.Addresses.Telephones.CellPhone
Does that make any sense? I can't seem to wrap it around my head.
I suggest you to mark all the classes, you need to grab, with custom attribute after that you could do something like this
class Program
{
static void Main(string[] args)
{
var lines = ExtractHelper.IterateProps(typeof(Container)).ToArray();
foreach (var line in lines)
Console.WriteLine(line);
Console.ReadLine();
}
}
static class ExtractHelper
{
public static IEnumerable<string> IterateProps(Type baseType)
{
return IteratePropsInner(baseType, baseType.Name);
}
private static IEnumerable<string> IteratePropsInner(Type baseType, string baseName)
{
var props = baseType.GetProperties();
foreach (var property in props)
{
var name = property.Name;
var type = ListArgumentOrSelf(property.PropertyType);
if (IsMarked(type))
foreach (var info in IteratePropsInner(type, name))
yield return string.Format("{0}.{1}", baseName, info);
else
yield return string.Format("{0}.{1}", baseName, property.Name);
}
}
static bool IsMarked(Type type)
{
return type.GetCustomAttributes(typeof(ExtractNameAttribute), true).Any();
}
public static Type ListArgumentOrSelf(Type type)
{
if (!type.IsGenericType)
return type;
if (type.GetGenericTypeDefinition() != typeof(List<>))
throw new Exception("Only List<T> are allowed");
return type.GetGenericArguments()[0];
}
}
[ExtractName]
public class Container
{
public string Name { get; set; }
public List<Address> Addresses { get; set; }
}
[ExtractName]
public class Address
{
public string AddressLine1 { get; set; }
public string AddressLine2 { get; set; }
public List<Telephone> Telephones { get; set; }
}
[ExtractName]
public class Telephone
{
public string CellPhone { get; set; }
}
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct, Inherited = true, AllowMultiple = true)]
public sealed class ExtractNameAttribute : Attribute
{ }
Per my comment, you could use something like this if it will always be a generic List type that you want to link to a child type. IteratePropertiesRecursively is an iterator over the properties of the given type, that will recursively enumerate the properties of the type and all child types linked through a generic List.
protected void Test()
{
Type t = typeof(Container);
string propertyList = string.Join(",", IteratePropertiesRecursively("", t).ToArray<string>());
// do something with propertyList
}
protected IEnumerable<string> IteratePropertiesRecursively(string prefix, Type t)
{
if (!string.IsNullOrEmpty(prefix) && !prefix.EndsWith(".")) prefix += ".";
prefix += t.Name + ".";
// enumerate the properties of the type
foreach (PropertyInfo p in t.GetProperties())
{
Type pt = p.PropertyType;
// if property is a generic list
if (pt.Name == "List`1")
{
Type genericType = pt.GetGenericArguments()[0];
// then enumerate the generic subtype
foreach (string propertyName in IteratePropertiesRecursively(prefix, genericType))
{
yield return propertyName;
}
}
else
{
// otherwise enumerate the property prepended with the prefix
yield return prefix + p.Name;
}
}
}
Note: This code will not correctly handle a type that recursively includes itself as a type of one of its properties. Trying to iterate over such a type will result in a StackOverflowException, as pointed out by #Dementic (thanks!).