I'm trying to parse to the following json string with NewtonSoft json 4.5:
{
"Name":"Henrik",
"Children":[
{
"Name":"Adam",
"Grandchildren":[
"Jessica", //How can i only show the value and not the property name?
"Michael"
]
}
]
}
My objects that i will serialize to json looks like this:
public class Parent
{
public string Name { get; set; }
[JsonProperty(PropertyName = "Children")]
public List<Child> Childs { get; set; }
}
public class Child {
public string Name { get; set; }
[JsonProperty(PropertyName = "Grandchildren")]
public List<GrandChild> GrandChilds { get; set; }
}
public class GrandChild {
[JsonProperty(PropertyName = "")]
public string Name { get; set; }
}
I've tried to set the property name to empty but it doesnt solve the issue.
You should replace List<GrandChild> with List<string> or else add a custom JsonConverter for GrandChild like so.
[JsonConverter(typeof(GrandChildConverter))]
public class GrandChild
{
public string Name { get; set; }
}
public class GrandChildConverter : JsonConverter
{
public override bool CanConvert(Type objectType)
{
return objectType == typeof(GrandChild);
}
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
writer.WriteValue(((GrandChild)value).Name);
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
return new GrandChild { Name = reader.Value.ToString() };
}
}
Related
I have model:
public class Department
{
public int DepartmentID { get; set; }
[Required]
[UniqueDepartmentName]
public string Name { get; set; }
public List<Person> Persons { get; set; }
}
And DBcontext:
public class InstituteContext : DbContext
{
public InstituteContext (DbContextOptions<InstituteContext> options)
: base(options)
{
}
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Department>().HasIndex(p => p.Name).IsUnique();
}
public DbSet<Institute.Models.Department> Department { get; set; }
}
As you see property "NAME" i make unique.
For validation i create My validation Attribute:
public class UniqueDepartmentName : ValidationAttribute
{
public override bool IsValid(object value)
{
var db = new InstituteContext();
}
}
But i can not create instance of InstituteContext, because constructor need parameters.
How i can create instance of InstituteContext? Or what should i pass to constructor in parameters?
Try this:
public class UniqueDepartmentName : ValidationAttribute
{
public override bool IsValid(object value)
{
var connectionString = "Your ConnectionString"
var options = new DbContextOptionsBuilder<InstituteContext>()
.UseSqlServer(new SqlConnection(connectionString)).Options;
using (var dbContext = new BloggingContext(options))
{
// Do necessary staffs here with dbContext
}
}
}
Your DbContextOptions method is in the wrong place, your constructor can be empty, and you need to add the method OnConfiguring, which receives the DbContextOptions.
Something like:
public DbSet<Department> Department { get; private set; }
protected override void OnConfiguring(DbContextOptionsBuilder options) {
// In my case I'm passing the connection string in this method below
options.UseSqlServer("Data Source=DATABASEIP;Initial Catalog=DATABASETABLE;" +
"User ID=USER;Password=PASSWORD");
}
I need to accept list of objects from user:
public async Task<IActionResult> CreateArticle(List<InformationBlockModel> informationBlocks)
{
...
}
ModelBinder should determine concrete types, but when I trying to cast InformationBlock to TextInformationBlock, exception throws.
Hierarchy:
public class InformationBlockModel
{
public virtual InformationBlockType Type { get; set; }
}
public class TextInformationBlockModel : InformationBlockModel
{
public string Text { get; set; }
public override InformationBlockType Type { get; set; } = InformationBlockType.Text;
}
public class ImageInformationBlockModel : InformationBlockModel
{
public override InformationBlockType Type { get; set; } = InformationBlockType.Image;
public string Name { get; set; }
}
Finally, I found a solution:
Startup.cs
services.AddMvc()
.AddJsonOptions(options => options.SerializerSettings.Converters.Add(new InformationBlockConverter()));
JsonCreationConverter.cs
public abstract class JsonCreationConverter<T> : JsonConverter
{
public override bool CanWrite { get; } = false;
public override bool CanRead { get; } = true;
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
}
protected abstract T Create(Type objectType, JObject jObject);
public override bool CanConvert(Type objectType)
{
return typeof(T) == objectType;
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue,
JsonSerializer serializer)
{
var jObject = JObject.Load(reader);
var target = Create(objectType, jObject);
serializer.Populate(jObject.CreateReader(), target);
return target;
}
}
InformationBlockConverter
public class InformationBlockConverter : JsonCreationConverter<InformationBlockModel>
{
private readonly Dictionary<InformationBlockType, Type> _types = new Dictionary<InformationBlockType, Type>
{
{InformationBlockType.Text, typeof(TextInformationBlockModel)},
{InformationBlockType.Image, typeof(ImageInformationBlockModel)},
{InformationBlockType.Video, typeof(VideoInformationBlockModel)}
};
protected override InformationBlockModel Create(Type objectType, JObject jObject)
{
return (InformationBlockModel) jObject.ToObject(_types[Enum.Parse<InformationBlockType>(
jObject.GetValue("type", StringComparison.InvariantCultureIgnoreCase).Value<string>(), true)]);
}
}
InformationBlockType
public enum InformationBlockType
{
Text,
Image,
Video
}
Asp.Net binding does not work like this by default. If you want to this sort of behaviour you will have to write your own custom model binding, which isn't too difficult.
Or, use a view model:
public class InformationBlockViewModel
{
public string Type { get; set; }
public string Text { get; set; }
public string Name { get; set; }
}
Then handle the block type in the controller:
public async Task<IActionResult> CreateArticle(List<InformationBlockViewModel> informationBlocks)
{
foreach (var block in informationBlocks) {
switch (block.Type)
{
case "Text":
// Handle text
break;
case "Image":
// Handle image
break;
case default:
throw new Exception("Unknown information block type.");
}
}
}
I want to bind only specified property of the WebAPI controller's model and skip all another properties to the DefaultModelBinder.
For example I have json, which should be passed to the WebAPI controller:
{
prop1: 1,
prop2: "b",
prop3: "c",
currentTime: "Fri Feb 27 2015",
references:
[
{ name: "Name 1", type: "Info" },
{ name: "Name 2", type: "Warn" }
]
}
On the server-side there is a HttpPost action with the following signature:
[HttpPost]
public void SaveObject(ReferenceModel model)
{
...
}
Where ReferenceModel is:
public class ReferenceModel
{
public int Prop1 {get; set;}
public string Prop2 {get; set;}
public string Prop3 {get; set;}
public DateTime CurrentTime {get; set;}
public List<BaseReferenceItem> References {get; set;}
}
I need for custom bind of References property, because I want to initialize this property at the runtime by objects of derived types.
For example if I will get Prop1 with value 1 so I need to initialize this collection by objects of type DerivedReferenceType1:
Model.References = new List<BaseReferenceItem>()
{
new DerivedReferenceType1(){...},
new DerivedReferenceType1(){...}
}
If I will get Prop1 with value 2 - of type DerivedReferenceType2 and so on.
I've found potential solution in the ModelBinder, but I don't want to bind all properties at the model.
I know that it's can be easy implemented in the MVC model binders with the call of
base.BindModel(controllerContext, bindingContext);
But how to do it in WebAPI?
Thanks!
One possibility would be to write a custom JsonConverter to handle this situation:
public class MyConverter : JsonConverter
{
public override bool CanConvert(Type objectType)
{
return objectType == typeof(ReferenceModel);
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
var jsonObject = JObject.Load(reader);
var result = new ReferenceModel();
serializer.Populate(jsonObject.CreateReader(), result);
result.References = new List<BaseReferenceItem>();
foreach (var obj in jsonObject.GetValue("references", StringComparison.InvariantCultureIgnoreCase))
{
if (result.Prop1 == 1)
{
result.References.Add(obj.ToObject<DerivedReferenceType1>(serializer));
}
else if (result.Prop1 == 2)
{
result.References.Add(obj.ToObject<DerivedReferenceType2>(serializer));
}
else
{
throw new NotSupportedException(result.Prop1 + " is not supported value for Prop1");
}
}
return result;
}
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
throw new NotImplementedException();
}
}
and then you could decorate your main view model with this custom converter:
[JsonConverter(typeof(MyConverter))]
public class ReferenceModel
{
public int Prop1 { get; set; }
public string Prop2 { get; set; }
public string Prop3 { get; set; }
public DateTime CurrentTime { get; set; }
public List<BaseReferenceItem> References { get; set; }
}
or if you prefer to do this without modifying your view models you could always register your custom converter during the bootstrap process:
public static void Register(HttpConfiguration config)
{
// Web API routes
...
var jsonFormatter = config.Formatters.JsonFormatter;
jsonFormatter.SerializerSettings.Converters.Add(new MyConverter());
}
I know that I can omit a property with JsonIgnore attribute or with IgnoreDataMember if my class it's decorated with DataContract attribute. In particulary I use the latter method but I see the property in json.
What's my error?
My class:
[DataContract(Name="Activity")]
public class ActivityDTO
{
[DataMember(Name = "id")]
public int Id { get; set; }
[DataMember(Name="usercode")]
[IgnoreDataMember]
public string UserCode { get; set; }
[DataMember(Name = "value")]
public string Value { get; set; }
}
and Json
{
"id": 5,
"usercode": "user",
"value": "100"
}
I want use usercode in deserialization and don't see it in serialization
The answer is in this article.
The IgnoreDataMemberAttribute attribute is only honored when used with unmarked types. This includes types that are not marked with one of the DataContractAttribute, SerializableAttribute, CollectionDataContractAttribute, or EnumMemberAttribute attributes, or marked as serializable by any other means (such as IXmlSerializable).
In your case, to omit the UserCode property - just do not mark with any serialization attributes:
[DataContract(Name="Activity")]
public class ActivityDTO
{
[DataMember(Name = "id")]
public int Id { get; set; }
public string UserCode { get; set; }
}
Added:
As you want deserialization to behave differently from serialization, I see 2 ways to achieve this:
a) described here: How can I prevent a datamember from being serialized
The disadvantage is that it is not thread-safe.
[DataContract( Name = "Activity" )]
public class ActivityDTO
{
private string _userCode;
[DataMember( Name = "id" )]
public int Id { get; set; }
[DataMember( Name = "usercode" , EmitDefaultValue = false )]
public string UserCode { get; set; }
[DataMember( Name = "value" )]
public string Value { get; set; }
[OnSerializing]
private void OnSerializing( StreamingContext context )
{
_userCode = UserCode;
UserCode = default( string );
}
[OnSerialized]
private void OnSerialized( StreamingContext context )
{
UserCode = _userCode;
}
}
b) you can go custom all the way and implement ISerializable:
[Serializable]
public class ActivityDTO : ISerializable
{
public int Id { get; set; }
public string UserCode { get; set; }
public string Value { get; set; }
public ActivityDTO()
{
}
public ActivityDTO( SerializationInfo info , StreamingContext context )
{
Id = info.GetInt32( "id" );
UserCode = info.GetString( "usercode" );
Value = info.GetString( "value" );
}
public void GetObjectData( SerializationInfo info , StreamingContext context )
{
info.AddValue( "id" , Id );
info.AddValue( "value" , Value );
}
}
I have a JSON string representing the following object type:
public partial class Address
{
public Address()
public string Country { get; set; }
public string StreetNo { get; set; }
public string City { get; set; }
public string Zip { get; set; }
public string Complement { get; set; }
public Nullable<int> Latitude { get; set; }
public Nullable<int> Longitude { get; set; }
public Nullable<int> Altitude { get; set; }
public string Url { get; set; }
public System.DateTime LastModified { get; set; }
public byte[] Version { get; set; }
public string StateCode { get; set; }
public string Street { get; set; }
public string RecordStatus { get; set; }
public int UserId { get; set; }
public int AddressId { get; set; }
public Dictionary<string, object> OriginalValues { get; set; }
}
I am trying to create a JsonConverter to deserialize the JSON string to the above object. I am stuck on what to add to my converter to create the dictionary within my code. The code for the converter thus far looks like this:
public class AddressConverter : JsonConverter
{
public override bool CanConvert(Type objectType)
{
var temp = objectType == typeof(Address);
return temp;
}
public override object ReadJson(JsonReader reader, Type objectType
, object existingValue, JsonSerializer serializer)
{
var obj = JObject.Load(reader);
var model = new Address();
model.Country = Convert.ToString(((JValue)obj["Country"]).Value);
model.StreetNo = Convert.ToString(((JValue)obj["StreetNo"]).Value);
model.City = Convert.ToString(((JValue)obj["City"]).Value);
model.Zip = Convert.ToString(((JValue)obj["Zip"]).Value);
model.Complement = Convert.ToString(((JValue)obj["Complement"]).Value);
model.Latitude = Convert.ToInt32(((JValue)obj["Latitude"]).Value);
model.Longitude = Convert.ToInt32(((JValue)obj["Longitude"]).Value);
model.Altitude = Convert.ToInt32(((JValue)obj["Altitude"]).Value);
model.Url = Convert.ToString(((JValue)obj["Url"]).Value);
model.LastModified = Convert.ToDateTime(((JValue)obj["LastModified"]).Value);
model.StateCode = Convert.ToString((((JValue)obj["Country"]).Value));
model.Street = Convert.ToString(((JValue)obj["StateCode"]).Value);
model.RecordStatus = Convert.ToString(((JValue)obj["RecordStatus"]).Value);
model.UserId = Convert.ToInt32(((JValue)obj["UserId"]).Value);
model.AddressId = Convert.ToInt32(((JValue)obj["AddressId"]).Value);
var encodedString = ((JValue) obj["Version"]).Value.ToString();
model.Version = Convert.FromBase64String(encodedString);
// add Dictionary code here
return model;
}
public override bool CanWrite
{
get
{
return false;
}
}
public override void WriteJson(JsonWriter writer, object value
, JsonSerializer serializer)
{
serializer.Serialize(writer, value);
throw new NotImplementedException();
}
}
I dont know what you are trying to acheive with this Custom convertor. But here is what you want.
// Dictionary code here
model.OriginalValues = (Dictionary<string, object>)JsonConvert.DeserializeObject(obj["OriginalValues"].ToString(), typeof(Dictionary<string, object>));