Deserialize a JObject to a list of .NET objects - c#

The task I have is to parse JSON into objects which can then create a form. This form can have sub-forms within it and it is this step I am having difficulty with.
Consider this JObject which is a representation of the sub-form and, because the main form has already been deserialized, is now passed as a JObject:
{
"3705": {
"type": "radioGroup",
"label": "Loft Insulation Applicable",
"default_value": null,
"order": "2",
"required": "false",
"security": "false",
"option": null,
"child": [
{
"type": "radio",
"label": "No",
"order": "3"
},
{
"type": "radio",
"label": "Yes",
"order": "4"
}
]
},
"3708": {
"type": "input",
"label": "Existing Depth (mm)",
"default_value": null,
"order": "5",
"required": "false",
"security": "false",
"option": null
},
"3709": {
"type": "input",
"label": "Required Depth (mm)",
"default_value": null,
"order": "6",
"required": "false",
"security": "false",
"option": null
},
"3715": {
"type": "radioGroup",
"label": "Total Pipework Meterage",
"default_value": null,
"order": "16",
"required": "false",
"security": "false",
"option": null,
"child": [
{
"type": "radio",
"label": "15 mm",
"order": "17"
},
{
"type": "radio",
"label": "22 mm",
"order": "18"
},
{
"type": "radio",
"label": "28 mm",
"order": "19"
}
]
},
"3719": {
"type": "formLabel",
"label": "Loft Access Requirements",
"default_value": null,
"order": "20",
"required": "false",
"security": "false",
"option": null
},
"3720": {
"type": "radioGroup",
"label": "Crawlers",
"default_value": null,
"order": "21",
"required": "false",
"security": "false",
"option": null,
"child": [
{
"type": "radio",
"label": "No",
"order": "22"
},
{
"type": "radio",
"label": "Yes",
"order": "23"
}
]
},
"3723": {
"type": "radioGroup",
"label": "Cavity Ladders for Loft",
"default_value": null,
"order": "24",
"required": "false",
"security": "false",
"option": null,
"child": [
{
"type": "radio",
"label": "No",
"order": "25"
},
{
"type": "radio",
"label": "Yes",
"order": "26"
}
]
},
"3726": {
"type": "image",
"label": "Loft Photos",
"default_value": null,
"order": "27",
"required": "false",
"security": "false",
"option": null
}
}
I am trying to Deserialize it to a list of objects of types defined in this class:
public class DBFormField
{
public string type { get; set; }
public string label { get; set; }
public string default_value { get; set; }
public string order { get; set; }
public string required { get; set; }
public DBFormFieldOption option = new DBFormFieldOption();
public List<DBFormFieldChild> child = new List<DBFormFieldChild>();
}
public class DBFormFieldOption
{
public List<DBFormFieldGrid> grid_rows = new List<DBFormFieldGrid>();
public List<DBFormFieldGrid> grid_columns = new List<DBFormFieldGrid>();
public string label1 { get; set; }
public string label2 { get; set; }
public string result_label { get; set; }
public string operation { get; set; }
public List<string> rows = new List<string>();
public string tab_id { get; set; }
}
public class DBFormFieldChild
{
public string type { get; set; }
public string label { get; set; }
public string order { get; set; }
}
public class DBFormFieldGrid
{
public string title { get; set; }
public string name { get; set; }
}
I have tried a few strategies to do this. The latest, I was trying this http://james.newtonking.com/json/help/CustomCreationConverter
For example:
List<DBFormField> parsedFields = new List<DBFormField>();
parsedFields = JsonConvert.DeserializeObject<List<DBFormField>>(someFormObj.form_fields.ToString, new FormConverter()); // ToString???
...
Public Class FormConverter
Inherits Converters.CustomCreationConverter(Of DBFormField)
Public Overrides Function Create(objectType As Type) As DBFormField
Return New DBFormField()
End Function
End Class
I suspect putting .ToString on the end of the JObject is not good enough for the DeserializeObject method? See comment above. But this code throws the error...
Cannot deserialize the current JSON object (e.g. {"name":"value"})
into type 'System.Collections.Generic.List`1[RSAP.DBFormField]'
because the type requires a JSON array (e.g. [1,2,3]) to deserialize
correctly.
I haven't tried serialising the JObject to a JSON string- this seems daft considering we have already parsed the JSON into a Jobject.

You've got single object in json and you try to deserialize it to list of objects - that can not be done.
Solutions:
You can wrap json object in array by adding [ and ] around object
You can deserialize to single object (not list):
JsonConvert.DeserializeObject<DBFormField>(someFormObj.form_fields.ToString(), new FormConverter());
You can deserialize single object and add it to list:
List<DBFormField> parsedFields = new List<DBFormField>();
parsedField = JsonConvert.DeserializeObject<DBFormField>someFormObj.form_fields.ToString(), new FormConverter());
parsedFields.Add(parsedField);

Related

Json.Net.Schema: How to generate a schema where only the [Required] properties are required?

I am writing a tool to work with resume.json files (project) and am creating a Json Schema to validate user input. I'm trying to automate this with Json.Net.Schema but the output always makes all properties required, regardless of whether the properties have the [Required] or [JsonRequired] attributes.
Schema Generation Code
var generator = new JSchemaGenerator
{
ContractResolver = new CamelCasePropertyNamesContractResolver(),
SchemaIdGenerationHandling = SchemaIdGenerationHandling.TypeName
};
var schema = generator.Generate(typeof(Resume));
var sb = new StringBuilder();
schema.WriteTo(new JsonTextWriter(
new IndentedTextWriter(
new StringWriter(sb),
" ")
)
{
Formatting = Formatting.Indented,
IndentChar = ' ', Indentation = 2,
QuoteName = true
}, new JSchemaWriterSettings
{
Version = SchemaVersion.Draft7
});
File.WriteAllText("E:\\resume.schema", sb.ToString());
Resume Class (and children)
public class Resume
{
[Required]
public Basics Basics { get; set; }
public Work[] Work { get; set; }
public Volunteer[] Volunteer { get; set; }
public Education[] Education { get; set; }
public Award[] Awards { get; set; }
public Publication[] Publications { get; set; }
public Skill[] Skills { get; set; }
public Language[] Languages { get; set; }
public Interest[] Interests { get; set; }
public Reference[] References { get; set; }
}
public class Award
{
[Required]
public string Title { get; set; }
public string Date { get; set; }
public string Awarder { get; set; }
public string Summary { get; set; }
}
public class Basics
{
[Required]
public string Name { get; set; }
public string Label { get; set; }
public Uri Picture { get; set; }
public string Email { get; set; }
public string Phone { get; set; }
public string Website { get; set; }
public string Summary { get; set; }
public Location Location { get; set; }
public Profile[] Profiles { get; set; }
}
public class Education
{
[Required]
public string Institution { get; set; }
public string Area { get; set; }
public string StudyType { get; set; }
public string StartDate { get; set; }
public string EndDate { get; set; }
public string Gpa { get; set; }
public string[] Courses { get; set; }
}
public class Interest
{
[Required]
public string Name { get; set; }
public string[] Keywords { get; set; }
}
public class Language
{
[Required]
public string language { get; set; }
[Required]
public string Fluency { get; set; }
}
public class Location
{
public string Address { get; set; }
[Required]
public string PostalCode { get; set; }
[Required]
public string City { get; set; }
[Required]
public string CountryCode { get; set; }
public string Region { get; set; }
}
public class Profile
{
[Required]
public string Network { get; set; }
[Required]
public string Username { get; set; }
[Required]
public string Url { get; set; }
}
public class Publication
{
[Required]
public string Name { get; set; }
public string Publisher { get; set; }
public string ReleaseDate { get; set; }
public string Website { get; set; }
public string Summary { get; set; }
}
public class Reference
{
[Required]
public string Name { get; set; }
public string reference { get; set; }
}
public class Skill
{
[Required]
public string Name { get; set; }
[Required]
public string Level { get; set; }
public string[] Keywords { get; set; }
}
public class Volunteer
{
[Required]
public string Organization { get; set; }
[Required]
public string Position { get; set; }
public string Website { get; set; }
public string StartDate { get; set; }
public string EndDate { get; set; }
public string Summary { get; set; }
public string[] Highlights { get; set; }
}
public class Work
{
[Required]
public string Company { get; set; }
[Required]
public string Position { get; set; }
public string Website { get; set; }
[Required]
public string StartDate { get; set; }
public string EndDate { get; set; }
public string Summary { get; set; }
public string[] Highlights { get; set; }
}
Current Output
{
"$schema": "http://json-schema.org/draft-07/schema#",
"$id": "Resume",
"definitions": {
"Award": {
"$id": "Award",
"type": [
"object",
"null"
],
"properties": {
"title": {
"type": "string"
},
"date": {
"type": [
"string",
"null"
]
},
"awarder": {
"type": [
"string",
"null"
]
},
"summary": {
"type": [
"string",
"null"
]
}
},
"required": [
"title",
"date",
"awarder",
"summary"
]
},
"Basics": {
"$id": "Basics",
"type": "object",
"properties": {
"name": {
"type": "string"
},
"label": {
"type": [
"string",
"null"
]
},
"picture": {
"type": [
"string",
"null"
],
"format": "uri"
},
"email": {
"type": [
"string",
"null"
]
},
"phone": {
"type": [
"string",
"null"
]
},
"website": {
"type": [
"string",
"null"
]
},
"summary": {
"type": [
"string",
"null"
]
},
"location": {
"$id": "Location",
"type": [
"object",
"null"
],
"properties": {
"address": {
"type": [
"string",
"null"
]
},
"postalCode": {
"type": "string"
},
"city": {
"type": "string"
},
"countryCode": {
"type": "string"
},
"region": {
"type": [
"string",
"null"
]
}
},
"required": [
"address",
"postalCode",
"city",
"countryCode",
"region"
]
},
"profiles": {
"$id": "Profile[]",
"type": [
"array",
"null"
],
"items": {
"$id": "Profile",
"type": [
"object",
"null"
],
"properties": {
"network": {
"type": "string"
},
"username": {
"type": "string"
},
"url": {
"type": "string"
}
},
"required": [
"network",
"username",
"url"
]
}
}
},
"required": [
"name",
"label",
"picture",
"email",
"phone",
"website",
"summary",
"location",
"profiles"
]
},
"Education": {
"$id": "Education",
"type": [
"object",
"null"
],
"properties": {
"institution": {
"type": "string"
},
"area": {
"type": [
"string",
"null"
]
},
"studyType": {
"type": [
"string",
"null"
]
},
"startDate": {
"type": [
"string",
"null"
]
},
"endDate": {
"type": [
"string",
"null"
]
},
"gpa": {
"type": [
"string",
"null"
]
},
"courses": {
"$id": "String[]",
"type": [
"array",
"null"
],
"items": {
"type": [
"string",
"null"
]
}
}
},
"required": [
"institution",
"area",
"studyType",
"startDate",
"endDate",
"gpa",
"courses"
]
},
"Interest": {
"$id": "Interest",
"type": [
"object",
"null"
],
"properties": {
"name": {
"type": "string"
},
"keywords": {
"$id": "String[]",
"type": [
"array",
"null"
],
"items": {
"type": [
"string",
"null"
]
}
}
},
"required": [
"name",
"keywords"
]
},
"Language": {
"$id": "Language",
"type": [
"object",
"null"
],
"properties": {
"language": {
"type": "string"
},
"fluency": {
"type": "string"
}
},
"required": [
"language",
"fluency"
]
},
"Location": {
"$ref": "Location"
},
"Profile": {
"$ref": "Profile"
},
"Publication": {
"$id": "Publication",
"type": [
"object",
"null"
],
"properties": {
"name": {
"type": "string"
},
"publisher": {
"type": [
"string",
"null"
]
},
"releaseDate": {
"type": [
"string",
"null"
]
},
"website": {
"type": [
"string",
"null"
]
},
"summary": {
"type": [
"string",
"null"
]
}
},
"required": [
"name",
"publisher",
"releaseDate",
"website",
"summary"
]
},
"Reference": {
"$id": "Reference",
"type": [
"object",
"null"
],
"properties": {
"name": {
"type": "string"
},
"reference": {
"type": [
"string",
"null"
]
}
},
"required": [
"name",
"reference"
]
},
"Skill": {
"$id": "Skill",
"type": [
"object",
"null"
],
"properties": {
"name": {
"type": "string"
},
"level": {
"type": "string"
},
"keywords": {
"$id": "String[]",
"type": [
"array",
"null"
],
"items": {
"type": [
"string",
"null"
]
}
}
},
"required": [
"name",
"level",
"keywords"
]
},
"Volunteer": {
"$id": "Volunteer",
"type": [
"object",
"null"
],
"properties": {
"organization": {
"type": "string"
},
"position": {
"type": "string"
},
"website": {
"type": [
"string",
"null"
]
},
"startDate": {
"type": [
"string",
"null"
]
},
"endDate": {
"type": [
"string",
"null"
]
},
"summary": {
"type": [
"string",
"null"
]
},
"highlights": {
"$id": "String[]",
"type": [
"array",
"null"
],
"items": {
"type": [
"string",
"null"
]
}
}
},
"required": [
"organization",
"position",
"website",
"startDate",
"endDate",
"summary",
"highlights"
]
},
"Work": {
"$id": "Work",
"type": [
"object",
"null"
],
"properties": {
"company": {
"type": "string"
},
"position": {
"type": "string"
},
"website": {
"type": [
"string",
"null"
]
},
"startDate": {
"type": "string"
},
"endDate": {
"type": [
"string",
"null"
]
},
"summary": {
"type": [
"string",
"null"
]
},
"highlights": {
"$id": "String[]",
"type": [
"array",
"null"
],
"items": {
"type": [
"string",
"null"
]
}
}
},
"required": [
"company",
"position",
"website",
"startDate",
"endDate",
"summary",
"highlights"
]
}
},
"type": "object",
"properties": {
"basics": {
"$ref": "Basics"
},
"work": {
"$id": "Work[]",
"type": [
"array",
"null"
],
"items": {
"$ref": "Work"
}
},
"volunteer": {
"$id": "Volunteer[]",
"type": [
"array",
"null"
],
"items": {
"$ref": "Volunteer"
}
},
"education": {
"$id": "Education[]",
"type": [
"array",
"null"
],
"items": {
"$ref": "Education"
}
},
"awards": {
"$id": "Award[]",
"type": [
"array",
"null"
],
"items": {
"$ref": "Award"
}
},
"publications": {
"$id": "Publication[]",
"type": [
"array",
"null"
],
"items": {
"$ref": "Publication"
}
},
"skills": {
"$id": "Skill[]",
"type": [
"array",
"null"
],
"items": {
"$ref": "Skill"
}
},
"languages": {
"$id": "Language[]",
"type": [
"array",
"null"
],
"items": {
"$ref": "Language"
}
},
"interests": {
"$id": "Interest[]",
"type": [
"array",
"null"
],
"items": {
"$ref": "Interest"
}
},
"references": {
"$id": "Reference[]",
"type": [
"array",
"null"
],
"items": {
"$ref": "Reference"
}
}
},
"required": [
"basics",
"work",
"volunteer",
"education",
"awards",
"publications",
"skills",
"languages",
"interests",
"references"
]
}
How do I get the schema generator to honor the Required attributes?
JSchemaGenerator has a property DefaultRequired:
Gets or sets the default required state of schemas.
For some reason, the default value for this property is Required.AllowNull, as shown in the source:
public JSchemaGenerator()
{
_schemaReferenceHandling = SchemaReferenceHandling.Objects;
_defaultRequired = Required.AllowNull;
}
If you change it to Required.Default, only those properties explicitly marked as required, e.g. with [Required] or [JsonProperty(Required = Required.Always)], will be listed as required in the schema:
var generator = new JSchemaGenerator
{
ContractResolver = new CamelCasePropertyNamesContractResolver(),
SchemaIdGenerationHandling = SchemaIdGenerationHandling.TypeName,
DefaultRequired = Required.Default,
};
The default value for DefaultRequired isn't documented, but probably should be. You might open an issue with Newtonsoft requesting a clarification to the documentation.
Demo fiddle here.

Validation of an invalid json data with Schema not failing with Deserialization

I am parsing the following JSON:
{"names":{"organizationNames":[{"name":"apple"}]}}
into the schema defined in C# Code as shown below.
public class QueryJson
{
#region Properties
[JsonProperty("organization")]
public HeOrganization Organization { get; set; }
[JsonProperty("names")]
public HeName Names { get; set; }
[JsonProperty("emails")]
public List<HeEmailAddress> Emails { get; set; }
#endregion
#region Linked Classes
public class HeOrganization
{
[JsonProperty("id")]
public Guid? ID { get; set; }
}
public class HeName
{
[JsonProperty("organizationNames")]
[Required(ErrorMessage = "Organization Name is Missing")]
public List<HeOrganizationName> OrganizationName { get; set; }
public class HeOrganizationName
{
[JsonProperty("name")]
[Required(ErrorMessage = "Name is Missing")]
public string Name { get; set; }
}
}
public class HeEmailAddress
{
[JsonProperty("address")]
[Required]
[EmailAddress]
public string Address { get; set; }
}
#endregion
}
If I were to pass an obviously invalid JSON:
{"names":{"organizationNames":[{"user":"apple"}]}}
I was expecting DeserializeObject() to fail or throw an Error, but instead it simply assigns 'Name' to null.
var myJson = JsonConvert.DeserializeObject<T>(jsonFilter);
Where T is the instance of the class.
Any suggestion on how to perform such Validations?
You could use Newtonsoft.Json.Schema to validate the schema of any given Json.
For example, for the class structure you have defined, an equivalent Schema could be generated as
var generator = new JSchemaGenerator();
var schema = generator.Generate(typeof(QueryJson));
The Generated Schema is as follows
{
"definitions": {
"HeEmailAddress": {
"type": [
"object",
"null"
],
"properties": {
"address": {
"type": "string",
"format": "email"
}
},
"required": [
"address"
]
},
"HeName": {
"type": [
"object",
"null"
],
"properties": {
"organizationNames": {
"type": "array",
"items": {
"$ref": "#/definitions/HeOrganizationName"
}
}
},
"required": [
"organizationNames"
]
},
"HeOrganization": {
"type": [
"object",
"null"
],
"properties": {
"id": {
"type": [
"string",
"null"
]
}
},
"required": [
"id"
]
},
"HeOrganizationName": {
"type": [
"object",
"null"
],
"properties": {
"name": {
"type": "string"
}
},
"required": [
"name"
]
}
},
"type": "object",
"properties": {
"organization": {
"$ref": "#/definitions/HeOrganization"
},
"names": {
"$ref": "#/definitions/HeName"
},
"emails": {
"type": [
"array",
"null"
],
"items": {
"$ref": "#/definitions/HeEmailAddress"
}
}
},
"required": [
"organization",
"names",
"emails"
]
}
You could now use the Schema to validate your jsons. For example, for the sample invalid json provided in OP
var invalidJson = #"{""names"":{""organizationNames"":[{""user"":""apple""}]}}";
var jsonInstance = JObject.Parse(invalidJson);
bool valid = jsonInstance.IsValid(schema); // False

ElasticSearch: Why all my text fields in the index have keyword type?

I pushing new documents using NEST in pair with class with attributes.
Here is how I define a class:
public class PatientNestModel
{
[Text]
public string FirstName { get; set; }
[Text]
public string LastName { get; set; }
[Text]
public string MiddleName { get; set; }
[Date(Format = "dd-MM-yyyy")]
public DateTime BirthdayDate { get; set; }
[Keyword]
public string Gender { get; set; }
[Text]
public string Phone { get; set; }
[Nested]
public List<AdditionalContact> AdditionalContacts { get; set; }
[Boolean]
public bool Active { get; set; }
}
Here is how I pushing it:
var response = _esClient.Index(model, idx => idx.Index("patients_esindex"));
But then my index metadata looks with keyword type.
{
"state": "open",
"settings": {
"index": {
"creation_date": "1543806292300",
"number_of_shards": "5",
"number_of_replicas": "1",
"uuid": "3_J5ck_CTaCLEdhIbCC0ZQ",
"version": {
"created": "6030199"
},
"provided_name": "patients_esindex"
}
},
"mappings": {
"patientnestmodel": {
"properties": {
"firstName": {
"type": "text",
"fields": {
"keyword": {
"ignore_above": 256,
"type": "keyword"
}
}
},
"lastName": {
"type": "text",
"fields": {
"keyword": {
"ignore_above": 256,
"type": "keyword"
}
}
},
"gender": {
"type": "text",
"fields": {
"keyword": {
"ignore_above": 256,
"type": "keyword"
}
}
},
"birthdayDate": {
"type": "date"
},
"phone": {
"type": "text",
"fields": {
"keyword": {
"ignore_above": 256,
"type": "keyword"
}
}
},
"active": {
"type": "boolean"
},
"middleName": {
"type": "text",
"fields": {
"keyword": {
"ignore_above": 256,
"type": "keyword"
}
}
}
}
}
},
"aliases": [],
"primary_terms": {
"0": 1,
"1": 1,
"2": 1,
"3": 1,
"4": 1
},
"in_sync_allocations": {
"0": [
"DCbu6-HvQT2ziCzhFZKU6A"
],
"1": [
"9SGADbBfSWuH7AanJUGgRA"
],
"2": [
"dPmhURTzTVWFV4z6Fh8ctw"
],
"3": [
"RHX67o0QQsueD6G67IXAkg"
],
"4": [
"aoBxi-i8Q1aVSeq1tT69Lw"
]
}
}
But then I am able to find the needed document by text search only if I used the term with .keyword
What am I do wrong?
Starting from ES 5.0, the string field has split into two new types: text, which should be used for full-text search, and keyword, which should be used for keyword search.
https://www.elastic.co/blog/strings-are-dead-long-live-strings
With Attribute mapping, you must call AutoMap() when creating the index, for the mapping from the attributes to be applied to your type in the index.
If the index has already been created, you can also use .Map<T>() with .AutoMap() to create the mapping for a type in the index, but this can only be done before indexing any documents (by default, Elasticsearch will infer the mapping from the first document indexed). If a mapping already exists for the type, you will either need to delete the index and start again, or reindex those documents into a new index that contains the expected mapping.

JSON Deserialization - handle property that can be a array

Question Background:
I am querying Amazons Product Advertising API and trying to desearlize the JSON response to a C# object.
The Issue:
I have received a deserialization error when the code attempts the conversion. I can't quite work out which part is causing the error though. The following shows the error I'm receiving:
Cannot deserialize the current JSON object (e.g. {"name":"value"}) into type'System.Collections.Generic.List`1[ShoppingComparisonEngine.AWS.AwsCommon.Item]' because the type requires a JSON array (e.g. [1,2,3]) to deserialize correctly.
To fix this error either change the JSON to a JSON array (e.g. [1,2,3]) or change the deserialized type so that it is a normal .NET type (e.g. not a primitive type like integer, not a collection type like an array or List<T>) that can be deserialized from a JSON object. JsonObjectAttribute can also be added to the type to force it to deserialize from a JSON object.
Path 'Items.Item.ASIN', line 1, position 612.
The code:
The following C# are what the Newtonsoft is attempting to serialize to.
Item.cs:
public class Item
{
[JsonProperty("asin")]
public string ASIN { get; set; }
[JsonProperty("parentasin")]
public string ParentASIN { get; set; }
[JsonProperty("detailpageurl")]
public string DetailPageURL { get; set; }
[JsonProperty("salesrank")]
public string SalesRank { get; set; }
[JsonProperty("smallimage")]
public SmallImage SmallImage { get; set; }
[JsonProperty("mediumimage")]
public MediumImage MediumImage { get; set; }
[JsonProperty("largeimage")]
public LargeImage LargeImage { get; set; }
[JsonProperty("imagesets")]
public ImageSets ImageSets { get; set; }
[JsonProperty("itemattributes")]
public ItemAttributes ItemAttributes { get; set; }
[JsonProperty("offersummary")]
public OfferSummary OfferSummary { get; set; }
[JsonProperty("offers")]
public Offers Offers { get; set; }
}
Items.cs:
public class Items
{
[JsonProperty("totalresults")]
public string TotalResults { get; set; }
[JsonProperty("totalpages")]
public string TotalPages { get; set; }
[JsonProperty("moresearchresultsurl")]
public string MoreSearchResultsUrl { get; set; }
[JsonProperty("item")]
public List<Item> Item { get; set; }
}
JSON:
I believe this is an issue with the fact I have a singe Item whereas the conversion requires a list of them?
{
"Items": {
"xmlns": "http://webservices.amazon.com/AWSECommerceService/2011-08-01",
"Request": {
"IsValid": "True",
"ItemSearchRequest": {
"Availability": "Available",
"Condition": "New",
"ItemPage": "6",
"Keywords": "champagne bucket",
"ResponseGroup": [
"Medium",
"Offers"
],
"SearchIndex": "HomeGarden"
}
},
"TotalResults": "51",
"TotalPages": "6",
"MoreSearchResultsUrl": "http://www.amazon.co.uk/gp/redirect.html?linkCode=xm2&SubscriptionId=AKIAIFERUZJXWJ3Y2USA&location=http%3A%2F%2Fwww.amazon.co.uk%2Fgp%2Fsearch%3Fkeywords%3Dchampagne%2Bbucket%26url%3Dsearch-alias%253Doutdoor&tag=compar0c2-21&creative=12734&camp=2025",
"Item": {
"ASIN": "B00IYO4HRQ",
"DetailPageURL": "http://www.amazon.co.uk/Bottle-Holder-Accessories-Garden-Maintenance/dp/B00IYO4HRQ%3FSubscriptionId%3DAKIAIFERUZJXWJ3Y2USA%26tag%3Dcompar0c2-21%26linkCode%3Dxm2%26camp%3D2025%26creative%3D165953%26creativeASIN%3DB00IYO4HRQ",
"ItemLinks": {
"ItemLink": [
{
"Description": "Add To Wishlist",
"URL": "http://www.amazon.co.uk/gp/registry/wishlist/add-item.html%3Fasin.0%3DB00IYO4HRQ%26SubscriptionId%3DAKIAIFERUZJXWJ3Y2USA%26tag%3Dcompar0c2-21%26linkCode%3Dxm2%26camp%3D2025%26creative%3D12734%26creativeASIN%3DB00IYO4HRQ"
},
{
"Description": "Tell A Friend",
"URL": "http://www.amazon.co.uk/gp/pdp/taf/B00IYO4HRQ%3FSubscriptionId%3DAKIAIFERUZJXWJ3Y2USA%26tag%3Dcompar0c2-21%26linkCode%3Dxm2%26camp%3D2025%26creative%3D12734%26creativeASIN%3DB00IYO4HRQ"
},
{
"Description": "All Customer Reviews",
"URL": "http://www.amazon.co.uk/review/product/B00IYO4HRQ%3FSubscriptionId%3DAKIAIFERUZJXWJ3Y2USA%26tag%3Dcompar0c2-21%26linkCode%3Dxm2%26camp%3D2025%26creative%3D12734%26creativeASIN%3DB00IYO4HRQ"
},
{
"Description": "All Offers",
"URL": "http://www.amazon.co.uk/gp/offer-listing/B00IYO4HRQ%3FSubscriptionId%3DAKIAIFERUZJXWJ3Y2USA%26tag%3Dcompar0c2-21%26linkCode%3Dxm2%26camp%3D2025%26creative%3D12734%26creativeASIN%3DB00IYO4HRQ"
}
]
},
"SmallImage": {
"URL": "http://ecx.images-amazon.com/images/I/31cr9r7XChL._SL75_.jpg",
"Height": {
"Units": "pixels",
"value": "75"
},
"Width": {
"Units": "pixels",
"value": "75"
}
},
"MediumImage": {
"URL": "http://ecx.images-amazon.com/images/I/31cr9r7XChL._SL160_.jpg",
"Height": {
"Units": "pixels",
"value": "160"
},
"Width": {
"Units": "pixels",
"value": "160"
}
},
"LargeImage": {
"URL": "http://ecx.images-amazon.com/images/I/31cr9r7XChL.jpg",
"Height": {
"Units": "pixels",
"value": "300"
},
"Width": {
"Units": "pixels",
"value": "300"
}
},
"ImageSets": {
"ImageSet": [
{
"Category": "swatch",
"SwatchImage": {
"URL": "http://ecx.images-amazon.com/images/I/31lBLzzkQlL._SL30_.jpg",
"Height": {
"Units": "pixels",
"value": "30"
},
"Width": {
"Units": "pixels",
"value": "30"
}
},
"SmallImage": {
"URL": "http://ecx.images-amazon.com/images/I/31lBLzzkQlL._SL75_.jpg",
"Height": {
"Units": "pixels",
"value": "75"
},
"Width": {
"Units": "pixels",
"value": "75"
}
},
"ThumbnailImage": {
"URL": "http://ecx.images-amazon.com/images/I/31lBLzzkQlL._SL75_.jpg",
"Height": {
"Units": "pixels",
"value": "75"
},
"Width": {
"Units": "pixels",
"value": "75"
}
},
"TinyImage": {
"URL": "http://ecx.images-amazon.com/images/I/31lBLzzkQlL._SL110_.jpg",
"Height": {
"Units": "pixels",
"value": "110"
},
"Width": {
"Units": "pixels",
"value": "110"
}
},
"MediumImage": {
"URL": "http://ecx.images-amazon.com/images/I/31lBLzzkQlL._SL160_.jpg",
"Height": {
"Units": "pixels",
"value": "160"
},
"Width": {
"Units": "pixels",
"value": "160"
}
},
"LargeImage": {
"URL": "http://ecx.images-amazon.com/images/I/31lBLzzkQlL.jpg",
"Height": {
"Units": "pixels",
"value": "300"
},
"Width": {
"Units": "pixels",
"value": "300"
}
}
},
{
"Category": "primary",
"SwatchImage": {
"URL": "http://ecx.images-amazon.com/images/I/31cr9r7XChL._SL30_.jpg",
"Height": {
"Units": "pixels",
"value": "30"
},
"Width": {
"Units": "pixels",
"value": "30"
}
},
"SmallImage": {
"URL": "http://ecx.images-amazon.com/images/I/31cr9r7XChL._SL75_.jpg",
"Height": {
"Units": "pixels",
"value": "75"
},
"Width": {
"Units": "pixels",
"value": "75"
}
},
"ThumbnailImage": {
"URL": "http://ecx.images-amazon.com/images/I/31cr9r7XChL._SL75_.jpg",
"Height": {
"Units": "pixels",
"value": "75"
},
"Width": {
"Units": "pixels",
"value": "75"
}
},
"TinyImage": {
"URL": "http://ecx.images-amazon.com/images/I/31cr9r7XChL._SL110_.jpg",
"Height": {
"Units": "pixels",
"value": "110"
},
"Width": {
"Units": "pixels",
"value": "110"
}
},
"MediumImage": {
"URL": "http://ecx.images-amazon.com/images/I/31cr9r7XChL._SL160_.jpg",
"Height": {
"Units": "pixels",
"value": "160"
},
"Width": {
"Units": "pixels",
"value": "160"
}
},
"LargeImage": {
"URL": "http://ecx.images-amazon.com/images/I/31cr9r7XChL.jpg",
"Height": {
"Units": "pixels",
"value": "300"
},
"Width": {
"Units": "pixels",
"value": "300"
}
}
}
]
},
"ItemAttributes": {
"Brand": "Garden at Home",
"EAN": "5971458914919",
"EANList": {
"EANListElement": "5971458914919"
},
"Feature": "Wine Bottle Holder and Cooler Bag Wine Accessories",
"Label": "Outdoor&Lawn",
"Manufacturer": "Outdoor&Lawn",
"MPN": "GG_89773368",
"PartNumber": "GG_89773368",
"ProductGroup": "Lawn & Patio",
"ProductTypeName": "OUTDOOR_LIVING",
"Publisher": "Outdoor&Lawn",
"Studio": "Outdoor&Lawn",
"Title": "Wine Bottle Holder and Cooler Bag Wine Accessories, Garden, Lawn, Maintenance"
},
"OfferSummary": {
"LowestNewPrice": {
"Amount": "5220",
"CurrencyCode": "GBP",
"FormattedPrice": "£52.20"
},
"TotalNew": "1",
"TotalUsed": "0",
"TotalCollectible": "0",
"TotalRefurbished": "0"
},
"Offers": {
"TotalOffers": "1",
"TotalOfferPages": "1",
"MoreOffersUrl": "http://www.amazon.co.uk/gp/offer-listing/B00IYO4HRQ%3FSubscriptionId%3DAKIAIFERUZJXWJ3Y2USA%26tag%3Dcompar0c2-21%26linkCode%3Dxm2%26camp%3D2025%26creative%3D12734%26creativeASIN%3DB00IYO4HRQ",
"Offer": {
"OfferAttributes": {
"Condition": "New"
},
"OfferListing": {
"OfferListingId": "kif7varXuUaD7y55JfdjU7h3YCJvQokyXfgDIWvxOx6%2FqiMbxoPBsyFWCmgo8pbOqZNcezFUCBSdlx3JtRpEpe4Tu7VGv9zncLOYVmoSOUEDNQf1rrKJzZVCjnVPRCKyes0GSlEzlx%2Faht4%2FtjijlvCo14Z3CCcm",
"Price": {
"Amount": "5800",
"CurrencyCode": "GBP",
"FormattedPrice": "£58.00"
},
"SalePrice": {
"Amount": "5220",
"CurrencyCode": "GBP",
"FormattedPrice": "£52.20"
},
"AmountSaved": {
"Amount": "580",
"CurrencyCode": "GBP",
"FormattedPrice": "£5.80"
},
"PercentageSaved": "10",
"Availability": "Usually dispatched within 1-2 business days",
"AvailabilityAttributes": {
"AvailabilityType": "now",
"MinimumHours": "24",
"MaximumHours": "48"
},
"IsEligibleForSuperSaverShipping": "0",
"IsEligibleForPrime": "0"
}
}
},
"EditorialReviews": {
"EditorialReview": {
"Source": "Product Description",
"Content": "Ice Bucket Bag and Wine Cooler This unique Ice Bucket Bag is attractive and very durable. It is ideal for transporting wine to a friend, hostess or party and arriving with it perfectly chilled.Transparent and waterproof, this wine or Champagne bag also makes a super, space-saving ice bucket that simply folds away after use. It is especially ideal for a picnic or BBQ as it can fold for easy transport and is unbreakableIn addition, this bag makes a great party favor - comes with pocket for place card, business card or gift card. Heavy duty plastic measures 4\" x 4\" x 10\" when open, gift card slot measures 2\" x 3\".",
"IsLinkSuppressed": "0"
}
}
}
}
EDIT:
This is the code I used to create a Converter to handle both a singular Item object and a List of them.
public class ItemConverter:JsonConverter
{
public override bool CanConvert(Type objectType)
{
throw new NotImplementedException();
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
if (reader.TokenType == JsonToken.StartArray)
{
List<Item> items = serializer.Deserialize<List<Item>>(reader);
return items;
}
else
{
Item itemList = serializer.Deserialize<Item>(reader);
return new List<Item>(new[] { itemList });
}
}
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
throw new NotImplementedException();
}
}
The Item property in the JSON is a plain object, not a list of objects. If it was supposed to be a list, then the JSON would be enclosed in square brackets to indicate it is an array, e.g.:
"Item": [{ item1 }, { item2}, ... ]
So you need to change your model to match. For example:
public class Items
{
//snip
public Item Item { get; set; }
}
.Net Fiddle: https://dotnetfiddle.net/9MACjN
You had multiple issues:
You are actually missing the closing } in your json.
You need to deserialize into a parent class (e.g., Response) that contains Items.
The Items.Item property needs to be of type Item, not List<Item>, as David said.
JsonProperty attributes are not needed at all.
Then it works.

How to delete an Item from the JSON file using UniqueId of clicked item in ListView?

I've a JSON file like this:
{
"Groups": [
{
"UniqueId": "1",
"Name": "England",
"Members": [
{
"UniqueId": "Rooney",
"Name": "Rooney",
"JerseyNumber": "10",
"Position": "Forward"
},
{
"UniqueId": "Aquero",
"Name": "Aguero",
"JerseyNumber": "16",
"Position": "Forward"
},
{
"UniqueId": "Nani",
"Name": "Nani",
"JerseyNumber": "7",
"Position": "Midfielder"
}
]
}
]
}
I've been able to reach down to the Members array of JSON by this code:
StorageFile file = await ApplicationData.Current.LocalFolder.GetFileAsync("Data2.json");
string jsonText = await FileIO.ReadTextAsync(file);
JsonObject jsonObject = JsonObject.Parse(jsonText);
JsonArray jsonArray = jsonObject["Groups"].GetArray()[0].GetObject()["Members"].GetArray();
I have the UniqueId of a Member and I want to search that member with UniqueId for its position within the "Members" array and then I want to delete that member. Suppose I've UniqueId="Nani", I want to search for that member with unique Id Nani and delete it.
I want the final result in JSON file as:
{
"Groups": [
{
"UniqueId": "1",
"Name": "England",
"Members": [
{
"UniqueId": "Rooney",
"Name": "Rooney",
"JerseyNumber": "10",
"Position": "Forward"
},
{
"UniqueId": "Aquero",
"Name": "Aguero",
"JerseyNumber": "16",
"Position": "Forward"
}
]
}
]
}
I think it will be better for you to create a class which matches your json tree. I am using .net 3.5 so i think i miss dll for json class you are using. But you will get the logic in this code below, comments will help you too
public class Groups // "Groups": [
{
public int UniqueId { get; set; } // "UniqueId": "1",
public String Name { get; set; } // "Name": "England",
public List<Member> ListMembers { get; set; } // "Members": [
public Groups(string json)
{
/* use your json object to get different data */
UniqueId = "Group UniqueId";
Name = "Group Name";
// get all member
foreach (jsonMember in jsonObject.Members)
{
Member member = new Member
{
UniqueId = "jsonMember UniqueId",
Name = "jsonMember Name",
JerseyNumber = "jsonMember JerseyNumber",
Position = "jsonMember Position",
};
ListMembers.Add(member);
}
}
public class Member // {"UniqueId":"Nani", "Name":"Nani", ... }
{
public string UniqueId { get; set; } // "UniqueId": "Aquero",
public String Name { get; set; } // "Name": "Aguero",
public int JerseyNumber { get; set; } // "JerseyNumber": "16",
public string Position { get; set; } // "Position": "Forward"
}
public void Delete(string UniqueId) // by example Delete("Nani")
{
ListMembers.RemoveAll(m => m.UniqueId == UniqueId);
}
}

Categories