XML string array deserialize - c#

I Have a XML string
<ItemAttributes>
<Binding>Misc.</Binding>
<Brand>Gilbert</Brand>
<Department>mens</Department>
<Feature>Elasticated waist with drawcord</Feature>
<Feature>Two pockets with reinforced stitching at base</Feature>
<Feature>Reinforced seams for strength in wear</Feature>
<Feature>Off set inside leg seam to reduce chaffing</Feature>
<Feature>100% Cotton Twill</Feature>
</ItemAttributes>
and class
[Serializable()]
public class ItemAttributes
{
public string Binding { get; set; }
public string Brand { get; set; }
public string Color { get; set; }
[XmlArrayItem("Feature")]
public string[] Feature { get; set; }
}
When I deserialize xml to object, "Feature" has no value at all. I want it to be an array of strings.

Try this:
[Serializable]
public class ItemAttributes
{
public string Binding { get; set; }
public string Brand { get; set; }
public string Color { get; set; }
[XmlElement(ElementName = "Feature")]
public string[] Feature { get; set; }
}

Related

Json.NET Deserialize Property Without Deserializing Parent Object Above It

I am trying to take this JSON:
{
"title":"string",
"description":"string",
"date":"2021-04-19T01:05:38.000Z",
"image":"url",
"images":[
"url1",
"url2"
],
"attributes":{
"phonebrand":"x",
"phonecarrier":"y",
"forsaleby":"z",
"price":12345,
"location":"daLocation",
"type":"OFFERED"
},
"url":"url to listing"
}
And convert it into this C# Object:
public class Listing {
[JsonProperty("title")]
public string Title { get; set; }
[JsonProperty("description")]
public string Description { get; set; }
[JsonProperty("date")]
public DateTime? Date { get; set; }
[JsonProperty("image")]
public string Image { get; set; }
[JsonProperty("images")]
public string[] Images { get; set; }
[JsonProperty("url")]
public string Url { get; set; }
[JsonProperty("price")]
public decimal Price { get; set; }
[JsonProperty("locationId")]
public int LocationId { get; set; }
[JsonProperty("categoryId")]
public int CategoryId { get; set; }
[JsonProperty("sortByName")]
public string SortByName { get; set; }
[JsonProperty("q")]
public string Q { get; set; }
[JsonProperty("location")]
public string Location { get; set; }
[JsonProperty("type")]
public string Type { get; set; }
[JsonProperty("forsaleby")]
public string ForSaleBy { get; set; }
[JsonProperty("fulfillment")]
public string Fulfillment { get; set; }
[JsonProperty("payment")]
public string Payment { get; set; }
[JsonProperty("phonebrand")]
public string? PhoneBrand { get; set; }
[JsonProperty("phonecarrier")]
public string? PhoneCarrier { get; set; }
}
My problem is, I'm trying to deserialize properties like price and phonebrand but those properties are under an object in the JSON. So when I try to deserialize them like this, those properties can't be found and are set as null. How can I deserialize those properties without changing my C# Class to include an Attributes class? I want to do this because I think that it is a cleaner/better design compared the JSON I'm taking in.
I suggest two approaches that are very explicit and easy to follow for the next developer looking at the code.
Two classes
creating a intermediate dto class that is used for deserialisation and then creating the business logic object from that intermediate object.
var withAttributes = Deserialise<ListingDto>();
var flatObject = new Listing(withAttributes);
One class
You could provide accessors at the top level which dip into the subclasses.
public class Listing
{
public AttributesDto Attributes {get; set}
...
public string Url => Attributes.Url; // Maybe '?.Url'
}

Deserializing Json from iTunes store into C# object

I have the following Json returned from iTunes (I have replaced some details for privacy such as username and review content)
{"feed":{"author":{"name":{"label":"iTunes Store"}, "uri":{"label":"http://www.apple.com/uk/itunes/"}}, "entry":[
{"author":{"uri":{"label":"https://itunes.apple.com/gb/reviews/ID"}, "name":{"label":"Username"}, "label":""}, "im:version":{"label":"3.51"}, "im:rating":{"label":"4"}, "id":{"label":"12345"}, "title":{"label":"Review title"}, "content":{"label":"Review contents", "attributes":{"type":"text"}}, "link":{"attributes":{"rel":"related", "href":"https://itunes.apple.com/gb/review?reviewid"}}, "im:voteSum":{"label":"0"}, "im:contentType":{"attributes":{"term":"Application", "label":"Application"}}, "im:voteCount":{"label":"0"}},
// more entries ...
I want to deserialize this into a class. I only need the Review Title, Review contents, and the "im:rating" (which is the number of stars). However, I'm struggling due to the nested Json and the use of keys of how to extract this data. So far I have created a class ready to deserialize, and I have AppStoreData appStoreData = JsonConvert.DeserializeObject<AppStoreData>(stringResult); to deserialize it
public class AppStoreData {
}
My problem is that I don't know what to put inside the class to get the info I need from the Json. I have tried things such as:
public string title {get; set;}
public string content {get; set;}
public string imrating {get; set;}
However this doesn't work. I also think im:rating is an invalid name in C#.
Can anyone please help? Thank you
To get around the issue with im:rating you can use a JsonProperty attribute
[JsonProperty("im:rating")]
public Id ImRating { get; set; }
The issue with converting things like Label to string property is that it's not a string but an object in the input file.
You need something like
[JsonProperty("title")]
public Id Title { get; set; }
where Id is a class
public class Id
{
[JsonProperty("label")]
public string Label { get; set; }
}
Or write a deserializer that converts it to string for you.
I would suggest trying out one of many free code generators like https://app.quicktype.io/?l=csharp or http://json2csharp.com/ to get a headstart and edit everything to your liking afterwards.
I suggest using json2csharp to convert the json to c# model and change the invalid attributes by adding JsonProperty
public class Name
{
public string label { get; set; }
}
public class Uri
{
public string label { get; set; }
}
public class Author
{
public Name name { get; set; }
public Uri uri { get; set; }
}
public class Uri2
{
public string label { get; set; }
}
public class Name2
{
public string label { get; set; }
}
public class Author2
{
public Uri2 uri { get; set; }
public Name2 name { get; set; }
public string label { get; set; }
}
public class ImVersion
{
public string label { get; set; }
}
public class ImRating
{
public string label { get; set; }
}
public class Id
{
public string label { get; set; }
}
public class Title
{
public string label { get; set; }
}
public class Attributes
{
public string type { get; set; }
}
public class Content
{
public string label { get; set; }
public Attributes attributes { get; set; }
}
public class Attributes2
{
public string rel { get; set; }
public string href { get; set; }
}
public class Link
{
public Attributes2 attributes { get; set; }
}
public class ImVoteSum
{
public string label { get; set; }
}
public class Attributes3
{
public string term { get; set; }
public string label { get; set; }
}
public class ImContentType
{
public Attributes3 attributes { get; set; }
}
public class ImVoteCount
{
public string label { get; set; }
}
public class Entry
{
public Author2 author { get; set; }
[JsonProperty(PropertyName = "im:version")]
public ImVersion imversion { get; set; }
[JsonProperty(PropertyName = "im:rating")]
public ImRating imrating { get; set; }
public Id id { get; set; }
public Title title { get; set; }
public Content content { get; set; }
public Link link { get; set; }
[JsonProperty(PropertyName = "im:voteSum")]
public ImVoteSum imvoteSum { get; set; }
[JsonProperty(PropertyName = "im:contentType")]
public ImContentType imcontentType { get; set; }
[JsonProperty(PropertyName = "im:voteCount")]
public ImVoteCount imvoteCount { get; set; }
}
public class Feed
{
public Author author { get; set; }
public List<Entry> entry { get; set; }
}
public class RootObject5
{
public Feed feed { get; set; }
}
Once the model is ready you can use JsonConvert.DeserializeObject to convert the JSON to c# object
using (StreamReader r = new StreamReader(filepath)) {
string json = r.ReadToEnd();
var newEmps = JsonConvert.DeserializeObject<RootObject5>(json);
Console.WriteLine(newEmps.feed.entry[0].imvoteCount.label);
}

Parse JSON Object / Array of Strings into new Custom Class

I'm trying to parse some JSON data and ultimatley store it in a database.
I'm having issues when storing a collection of strings / values which are not objects themselves.
For example - The "callingCodes" & "altSpellings"
I want to store these in an SQL table which will have reference to the country they belong to.
This is an example of the JSON:
{
"name":"Puerto Rico",
"topLevelDomain":[
".pr"
],
"alpha2Code":"PR",
"alpha3Code":"PRI",
"callingCodes":[
"1787",
"1939"
],
"capital":"San Juan",
"altSpellings":[
"PR",
"Commonwealth of Puerto Rico",
"Estado Libre Asociado de Puerto Rico"
],
"region":"Americas",
"subregion":"Caribbean",
"population":3474182,
"latlng":[
18.25,
-66.5
]
},
I originally created some C# classes to represent the data using http://json2csharp.com/
This sugguested I store the values as a list of strings, which I did:
public List<string> CallingCodes { get; set; }
I now want to store the data in a table, so I created a class "TopLevelDomain" to store / link the data to the parent country:
public class CallingCode
{
public int ID { get; set; }
public int CountryID { get; set; }
public string Code{ get; set; }
}
So I altered the parent to be as follows:
public ICollection<CallingCode> CallingCodes { get; set; }
Is it possible to direct the string values into the "Code" property of my new class?
Or am I trying to crowbar two pieces of logic into one?
Is the correct way to have models for the JSON, and manually restructure these into my new DB / Entity Framework Models?
This is the auto-generated class you get from such JSON. The tricky bit here is List of primitive types.
public class RootObject
{
public string name { get; set; }
public List<string> topLevelDomain { get; set; }
public string alpha2Code { get; set; }
public string alpha3Code { get; set; }
public List<string> callingCodes { get; set; }
public string capital { get; set; }
public List<string> altSpellings { get; set; }
public string region { get; set; }
public string subregion { get; set; }
public int population { get; set; }
public List<double> latlng { get; set; }
}
Certain databases like PostgreSQL supports array as primitive type. If you are using PostgreSQL then you can perhaps make those properties array of primitive type and store them on server as is.
For other databases which does not support array, you cannot store a list of primitive values into single column of database. The easiest way to deal with it is to introduce serialization and create single string which can be stored to server. So looking at above class, for public List<string> topLevelDomain property, you can rewrite it in following way,
[NotMapped]
public List<string> topLevelDomain
{
get => Deserialize(TopLevelDomainString);
set => TopLevelDomainString = Serialize(value);
}
public string TopLevelDomainString { get; set; }
With NotMapped attribute EF will not map topLevelDomain property. But TopLevelDomainString will be persisted to database and it will get values from topLevelDomain. As for Serialize/Deserialize methods, you can use any serialization method. You can use JsonSerializer directly (since you are already using JSON objects. Or you can just combine strings using , as delimiter and split string from server using it.
Starting with EF Core 2.1 version, you can use Value-Conversion feature directly to provide funcs to do conversion (essentially serialization code like above) to EF and EF will do it while reading/saving data from/to server. This will avoid you having to create additional CLR property.
Here is your auto-generated class:
public class RootObject
{
public string name { get; set; }
public List<string> topLevelDomain { get; set; }
public string alpha2Code { get; set; }
public string alpha3Code { get; set; }
public List<string> callingCodes { get; set; }
public string capital { get; set; }
public List<string> altSpellings { get; set; }
public string region { get; set; }
public string subregion { get; set; }
public int population { get; set; }
public List<double> latlng { get; set; }
}
Let's prepare another simple one:
public class MyRootObject
{
public MyRootObject(RootObject root)
{
Name = root.name;
List<CallingCode> callingCodesConverted = new List<CallingCode>();
foreach (string code in root.callingCodes)
{
CallingCode newCode = new CallingCode() { Code = code };
callingCodesConverted.Add(newCode);
}
CallingCodes = callingCodesConverted;
}
public string Name { get; set; }
public List<CallingCode> CallingCodes { get; set; }
}
Now you could first do encoding from json to class RootObject, and then create MyRootObject based on it:
string path = #"D:\test.txt";
var r = new StreamReader(path);
var myJson = r.ReadToEnd();
RootObject root = Json.Decode<RootObject>(myJson);
MyRootObject myroot = new MyRootObject(root);
Sure MyRootObject is only an example.
Is the correct way to have models for the JSON, and manually
restructure these into my new DB / Entity Framework Models?
Well some might use that in their code and some might do even worse than that. But, I personally like to use models/dtos for the things I could and know about the data.
am I trying to crowbar two pieces of logic into one?
Yes. But, it depends. Either strongly type/define objects and all or just Ser/Deser everytime.
Your data is known (no unkown properties or anything, so why serialize and deserialize it everytime?)
Solution 1 : if you use the JSON as is to create a DB Entry
Entites
public class Country //assuming this is country data
{
public int Id { get; set; }
[JsonProperty("name")]
public string Name { get; set; }
[JsonProperty("alpha2Code")]
public string Alpha2Code { get; set; }
[JsonProperty("alpha3Code")]
public string Alpha3Code { get; set; }
[JsonProperty("capital")]
public string Capital { get; set; }
[JsonProperty("region")]
public string Region { get; set; }
[JsonProperty("subregion")]
public string Subregion { get; set; }
[JsonProperty("population")]
public long Population { get; set; }
[JsonProperty("topLevelDomain")]
public virtual List<TopLevelDomain> TopLevelDomains { get; set; }
[JsonProperty("callingCodes")]
public virtual List<CallingCodes> CallingCodes { get; set; }
[JsonProperty("altSpellings")]
public virtual List<AltSpellings> AltSpellings { get; set; }
[JsonProperty("latlng")]
public virtual List<Coordinates> Coordinates { get; set; }
}
public class TopLevelDomain
{
public int Id { get; set; }
[ForeignKey("Country")]
public int CountryId {get; set; }
public virtual Country Country { get; set; }
public string DomainName { get; set; }
}
public class CallingCodes
{
public int Id { get; set; }
[ForeignKey("Country")]
public int CountryId {get; set; }
public virtual Country Country { get; set; }
public string Code { get; set;} // either store it as String
//OR
public long Code { get; set;}
}
public class AltSpellings
{
public int Id { get; set; }
[ForeignKey("Country")]
public int CountryId {get; set; }
public virtual Country Country { get; set; }
public string AltSpelling { get; set; }
}
public class Coordinates
{
public int Id { get; set; }
[ForeignKey("Country")]
public int CountryId {get; set; }
public virtual Country Country { get; set; }
public double Coordinates { get; set; } //again either as string or double, your wish. I would use double
}
Use it like so
//assuming using Newtonsoft
var myJson = ....assuming one Country;
var myJsonList = ...assuming List<Country>;
var country = JsonConvert.DeserializeObject<Country>(myJson);
var countries = JsonConvert.DeserializeObject<List<Country>>(myJson);
Solution 2 : First one can cause too many tables for a little data but First solution is a little more object based and typed, so Here is another one
Entity
public class Country //assuming this is country data
{
public int Id { get; set; }
public string Name { get; set; }
public string Alpha2Code { get; set; }
public string Alpha3Code { get; set; }
public string Capital { get; set; }
public string Region { get; set; }
public string Subregion { get; set; }
public long Population { get; set; }
public string TopLevelDomains { get; set; }
public string CallingCodes { get; set;}
public string AltSpellings { get; set; }
public double Longitude { get; set;}
public double Latitude { get; set; }
}
Model
public class CountryJson
{
public int Id { get; set; }
[JsonProperty("name")]
public string Name { get; set; }
[JsonProperty("alpha2Code")]
public string Alpha2Code { get; set; }
[JsonProperty("alpha3Code")]
public string Alpha3Code { get; set; }
[JsonProperty("capital")]
public string Capital { get; set; }
[JsonProperty("region")]
public string Region { get; set; }
[JsonProperty("subregion")]
public string Subregion { get; set; }
[JsonProperty("population")]
public long Population { get; set; }
[JsonProperty("topLevelDomain")]
public List<string> TopLevelDomains { get; set; }
[JsonProperty("callingCodes")]
public List<string> CallingCodes { get; set;}
[JsonProperty("altSpellings")]
public List<string> AltSpellings { get; set; }
[JsonProperty("latlng")]
public List<string> Latlng { get; set; }
}
Use it like so
//assuming using Newtonsoft
var myJson = ....assuming one Country;
var countryJson = JsonConvert.DeserializeObject<CountryJson>(myJson);
//Write a Mapper Or Manual Map like below
var countryEntity = new Country
{
Name = countryJson.Name,
...
TopLevelDomains = JsonConvert.Serialize(countryJson.TopLevelDomains),
CallingCodes = JsonConvert.Serialize(countryJson.CallingCodes),
...//same for all list (NOTE: YOU NEED TO DESERIALIZE IT WHEN YOU FETCH IT FROM DB
Longitude = countryJson.Latlng.ElementAt(0),//assuming 0 is longi, 1 is lat
Latitude = countryJson.Latlng.ElementAt(1)//you can do it like above as well as string if you want
}

Is there a way to associate XML Tags with blocks of properties?

For XMLSerialization, You use XML Tags to indicate how a property/field is to be added in the serialization.
Its ugly having an XML tag for each property. I understand the value of having the explicitness, but its a pain on the eyes
My question is there a method to do something like this, where a tag is associated with a block?
public class Application : IApplication
{
[XmlAttribute]
public string Name { get; set; }
public string Source { get; set; }
[XmlElement]
public string Description { get; set; }
public string Icon { get; set; }
public string StoreLink { get; set; }
[XmlIgnore]
public string Branch { get; set; }
public string Version { get; set; }
public string Build { get; set; }
public bool IsDebug { get; set; }
public bool IsX86 { get; set; }
public override string ToString()
{
return Name;
}
}
Apperently you can do...
[XmlAttribute] public string Version;
[XmlAttribute] public string Number;
Which is a bit easier on the eyes.

C# XMLNS complicated deserialization

I'm trying to deserialize XMLNS file.
<feed xmlns:live="http://www.live.com/marketplace" xmlns="http://www.w3.org/2005/Atom">
<live:totalItems>1177</live:totalItems>
<live:numItems>1</live:numItems>
<title>FindGames Results</title>
<updated>2013-09-19T09:28:02.77Z</updated>
<entry live:itemNum="34" live:detailView="3">
<id>urn:uuid:66ACD000-77FE-1000-9115-D802555308C3</id>
<updated>2013-09-19T09:28:02.77Z</updated>
<title>Rayman® Legends</title>
<content type="text">game content</content>
<live:media>
<live:mediaType>1</live:mediaType>
<live:gameTitleMediaId>urn:uuid:66ACD000-77FE-1000-9115-D802555308C3</live:gameTitleMediaId>
<live:reducedTitle>Rayman® Legends</live:reducedTitle>
<live:reducedDescription>The Glade of Dreams is in trouble once again! During a 100-year nap, nightmares have multiplied and spread, creating new monsters even scarier than before!</live:reducedDescription>
<live:availabilityDate>2013-06-11T00:00:00</live:availabilityDate>
<live:releaseDate>2013-08-29T00:00:00</live:releaseDate>
<live:ratingId>20</live:ratingId>
<live:developer>Ubisoft</live:developer>
<live:publisher>Ubisoft</live:publisher>
<live:newestOfferStartDate>2013-09-13T09:00:00Z</live:newestOfferStartDate>
<live:totalOfferCount>1</live:totalOfferCount>
<live:titleId>1431505091</live:titleId>
<live:effectiveTitleId>1431505091</live:effectiveTitleId>
<live:gameReducedTitle>Rayman® Legends</live:gameReducedTitle>
<live:ratingAggregate>4.50</live:ratingAggregate>
<live:numberOfRatings>1193</live:numberOfRatings>
</live:media>
</entry>
</feed>
My current code:
Deserialize class:
[XmlRoot("feed", Namespace = "http://www.w3.org/2005/Atom")]
public class entry
{
public string title { get; set; }
public string totalItems { get; set; }
public string reducedDescription{ get; set; }
public string ratingId { get; set; }
public string developer { get; set; }
public string publisher { get; set; }
public string tittleId { get; set; }
public string ratingAggregate { get; set; }
public string numberOfRatings { get; set; }
public string boxImage { get; set; }
public string categories { get; set; }
}
class deserializeXML
{
public static void deserialize()
{
using (StreamReader reader = new StreamReader("Query.xml"))
{
XmlSerializer serializer = new XmlSerializer(typeof(entry));
entry x1 = serializer.Deserialize(reader) as entry;
}
}
}
I just receiving title (FindGames Results instead of Rayman® Legends).
I need to get parameters after "entry" and "live:media", i have no idea how to receive parameters with "live:" at the beginning.
EDIT:
[XmlElement(Namespace = "http://www.live.com/marketplace")]
public int numItems { get; set; }
[XmlElement(Namespace = "http://www.live.com/marketplace")]
public int totalItems { get; set; }
It's working well, but:
[XmlElement("media" Namespace="http://www.live.com/marketplace")]
public media media{ get; set; }
This returning null media class :/
You need to explicitly map those types and properties that are not in the atom namespace to their respective namespaces. The namespace declaration in the XmlRoot just specifies the namespace of the root element. If you have multiple namespaces in the document you need to call them out explicitly.
For instance to map the itemNum attribute correctly try this in your entry class:
[XmlRoot("feed", Namespace = "http://www.w3.org/2005/Atom")]
public class entry
{
[XmlAttribute("itemNum", Namespace = "http://www.live.com/marketplace")]
public int itemNum { get; set; }
public string title { get; set; }
public string totalItems { get; set; }
public string reducedDescription { get; set; }
public string ratingId { get; set; }
public string developer { get; set; }
public string publisher { get; set; }
public string tittleId { get; set; }
public string ratingAggregate { get; set; }
public string numberOfRatings { get; set; }
public string boxImage { get; set; }
public string categories { get; set; }
}
You then need to declare additional type to map into things like the media node.
class media
{
public int mediaType{ get; set; }
public string reducedDescription{ get; set; }
etc. etc.
}
You then need to move all of those "live" properties from your entry class into you new media class leaving you with an entry class that might look like this:
[XmlRoot("feed", Namespace = "http://www.w3.org/2005/Atom")]
public class entry
{
[XmlAttribute("itemNum", Namespace = "http://www.live.com/marketplace")]
public int itemNum { get; set; }
public string title { get; set; }
[XmlElement("media" Namespace="http://www.live.com/marketplace")]
public media media{ get; set; }
}
Your class hierarchy does not need to exactly match the xml hierarchy and you can do some significant remapping and transformation of the Xml using the various attributes in System.Xml.Serialization however I find it easier to implement, understand and maintain this sort of stuff if the C# classes map 1:1 onto the xml structure.

Categories