Deserializing XML Based on Element Attributes - c#

I am trying to deserialize an XML file within a C# program that looks like this:
<?xml version="1.0" encoding="utf-8" ?>
<Addresses>
<ListName>Flowers</ListName>
<Address contextRef="RP.CC">Some Address</Address>
<Address contextRef="RP.BE">Some Other Address</Address>
<Address contextRef="RP.BV">Yet Another Address</Address>
<Address contextRef="RP.CAL">Wow, I Can't Believe It's Another Address</Address>
</Addresses>
I do not have any control over the format of this file. But, it will always have some combination of these 4 Address elements (i.e. these 4 contextRef attribute values are the only ones used) with differing element values each time.
Now, instead of deserializing into an Address array, I need to send them to individual properties within an Addresses object. My Current implementation uses an array and then a setter method to set these properties based on the contextRef as so:
public class Addresses
{
[XmlElement("ListName")]
public string ListName { get; set; }
private Address[] _addresses;
[XmlElement("Address")]
public Address[] AddressesArray
{
get
{
return _addresses;
}
set
{
_addresses = value;
SetAddress();
}
}
[XmlIgnore]
public Address AddressG21 { get; set; }
[XmlIgnore]
public Address AddressG22 { get; set; }
[XmlIgnore]
public Address AddressG23 { get; set; }
[XmlIgnore]
public Address AddressG9 { get; set; }
private void SetAddress()
{
foreach (var address in _addresses)
{
if (address.ContextRef == "RP.CC")
{
AddressG21 = address;
}
else if (address.ContextRef == "RP.BE")
{
AddressG22 = address;
}
else if (address.ContextRef == "RP.BV")
{
AddressG23 = address;
}
else if (address.ContextRef == "RP.CAL")
{
AddressG9 = address;
}
}
}
}
Where the Address object is defined as so:
public class Address
{
private string valueField;
/// <remarks/>
[XmlText]
public string Value
{
get
{
return this.valueField;
}
set
{
this.valueField = value;
}
}
[XmlAttribute("contextRef")]
public string ContextRef { get; set; }
}
So, my question is, is there a neater/better way of deserializing this XML directly into the AddressG21, etc. object properties without first using the Address array?
Thanks in advance.

I would use xml linq and create a dictionary in the class
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml;
using System.Xml.Linq;
namespace ConsoleApplication1
{
class Program
{
const string FILENAME = #"c:\temp\test.xml";
static void Main(string[] args)
{
XDocument doc = XDocument.Load(FILENAME);
Addresses addresses = doc.Descendants("Addresses").Select(x => new Addresses() {
ListName = (string)x.Element("ListName"),
dict = x.Elements("Address")
.GroupBy(y => (string)y.Attribute("contextRef"), z => (string)z)
.ToDictionary(y => y.Key, z => z.FirstOrDefault())
}).FirstOrDefault();
}
}
public class Addresses
{
public string ListName { get; set; }
public Dictionary<string, string> dict { get; set; }
}
}
If you had multiple Addresses elements then use this
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml;
using System.Xml.Linq;
namespace ConsoleApplication1
{
class Program
{
const string FILENAME = #"c:\temp\test.xml";
static void Main(string[] args)
{
XDocument doc = XDocument.Load(FILENAME);
List<Addresses> addresses = doc.Descendants("Addresses").Select(x => new Addresses() {
ListName = (string)x.Element("ListName"),
dict = x.Elements("Address")
.GroupBy(y => (string)y.Attribute("contextRef"), z => (string)z)
.ToDictionary(y => y.Key, z => z.FirstOrDefault())
}).ToList();
}
}
public class Addresses
{
public string ListName { get; set; }
public Dictionary<string, string> dict { get; set; }
}
}

Related

How do I call JSON in a simplified manner

I am connecting to an external API which seems to be returning JSON
using (var client = new APIClient())
{
var data = client.General.GetAccountInfo().Data.Balances;
}
When I move over .Data.Balances, it shows:
IEnumerable<API.Net.Objects.Spot.SpotData.APIBalance>
API.Net.Objects.Spot.SpotData.APIAccountInfo.Balances { get; set; }
List of assets and their current balances
Here is an extract of the JSON data:
"balances":[
{
"asset":"ABC",
"free":"0.00000000",
"locked":"0.00000000"
},
{
"asset":"DEF",
"free":"0.00000000",
"locked":"0.00000000"
},
{
"asset":"GHI",
"free":"0.00000000",
"locked":"0.00000000"
}
]
How do I make use of this data so if I type console.writeline(data[0]["asset"]), it gives me ABC?
This seems to be the simplist solution:
using System.Linq;
using (var client = new APIClient())
{
var data = client.General.GetAccountInfo().Data.Balances.ToList();
Console.WriteLine(data[0].asset);
Console.WriteLine(data[0].free);
Console.WriteLine(data[0].locked);
}
Hy,
From the sample file you can create a class ´balance »
Public class balance
{
Public string asset {get; set;}
Public Free .......
Public Locked .....
}
And then you can use Json.net
To deserialize the JSon file
public void serializejson()
{
List balances = JsonConvert.DeserializeObject<List>(data);
}
}
using System;
using System.Collections.Generic;
using System.IO;
using System.Text.Json;
using System.Text.Json.Serialization;
namespace Game
{
public class Balance
{
[JsonPropertyName("asset")]
public string Asset { get; set; }
[JsonPropertyName("free")]
public string Free { get; set; }
[JsonPropertyName("locked")]
public string Locked { get; set; }
}
public class Root
{
[JsonPropertyName("balances")]
public List<Balance> Balances { get; set; }
}
class Program
{
static void Main(string[] args)
{
var data = File.ReadAllText("test.json");
var deserialized = JsonSerializer.Deserialize<Root>(data);
var balances = deserialized.Balances;
// Don't iterati in such a way! Use foreach instead.
for (int i = 0; i < balances.Count; i++)
{
Console.WriteLine($"{balances[i].Asset} {balances[i].Free} {balances[i].Locked}");
}
foreach (var balance in balances)
{
Console.WriteLine($"{balance.Asset} {balance.Free} {balance.Locked}");
}
}
}
}
You can use this code for you:
public class Balance {
public string asset { get; set; }
public string free { get; set; }
public string locked { get; set; }
}
public class Root {
public List<Balance> balances { get; set; }
}
And for deserialize:
using (var client = new APIClient())
{
var data = client.General.GetAccountInfo().Data.Balances;
Root myDeserializedClass = JsonConvert.DeserializeObject<Root>(data);
Console.WriteLine(myDeserializedClass.balances[0].asset);
}

Getting parts of a repetitive string in C#

Extracting parts of a string is a common question, but here the string content, e.g. ' "main\":{\"temp\": ' is repetitive:
string test = "{\"cod\":\"200\",\"message\":0,\"cnt\":40,\"list\":[{\"dt\":1576810800,\"main\":{\"temp\":288.19,\"feels_like\":284.44,\"temp_min\":288.19,\"temp_max\":291.53,\"{\"dt\":1576821600,\"main\":{ \"temp\":283.97,\"feels_like\":281.56,\"temp_min\":283.97,\"temp_max\":286.47,\"pressure\":1007,\"sea_level\":1007,\"grnd_level\":997,\"humidity\":93,\"temp_kf\":-2.5},\"weather\":[{\"id\":501,\"main\":\"Rain\",\"description\":\"moderate rai\",\"icon\":\"10d\"}],\"clouds\":{\"all\":90},\"wind\"";
Using
string s = test.Substring(test.IndexOf("\"main\":{\"temp\":") + 15);
I get
288.19,"feels_like":284.44,,"temp_min":288.19,"temp_max":291.53,"{"dt":157682160
0,"main":{ "temp":283.97,"feels_like":281.56,"temp_min":283.97,"temp_max":286.47
,"pressure":1007,"sea_level":1007,"grnd_level":997,"humidity":93,"temp_kf":-2.5}
,"weather":[{"id":501,"main":"Rain","description":"moderate rain","icon":"10d"}],
"clouds":{"all":90},"wind"
instead of
288.19
Any clue what command would help? Should be something that can be adapted to ' "temp_max" ' or ' "pressure" '.
Cheers
Thanks to Mohammed, here's the working code spitting out all tempeatures:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Net;
using Newtonsoft.Json;
namespace ConsoleApp2
{
public class RootObject
{
public List<MyList> list { get; set; }
}
public class MyList
{
public Main main { get; set; }
}
public class Main
{
public double temp { get; set; }
}
class Program
{
static void Main()
{
using (WebClient client = new WebClient())
{
Console.WriteLine("ACCESSING ...");
string test = client.DownloadString("http://api.openweathermap.org/data/2.5/forecast?q=Auckland,NZ&APPID=45c3e583468bf450fc17026d6734507e");
//string test = "{\"cod\":\"200\",\"message\":0,\"cnt\":40,\"list\":[{\"dt\":1576810800,\"main\":{\"temp\":288.19,\"feels_like\":284.44,\"temp_min\":288.19,\"temp_max\":291.53,\"{\"dt\":1576821600,\"main\":{ \"temp\":283.97,\"feels_like\":281.56,\"temp_min\":283.97,\"temp_max\":286.47,\"pressure\":1007,\"sea_level\":1007,\"grnd_level\":997,\"humidity\":93,\"temp_kf\":-2.5},\"weather\":[{\"id\":501,\"main\":\"Rain\",\"description\":\"moderate rai\",\"icon\":\"10d\"}],\"clouds\":{\"all\":90},\"wind\"";
var myobject = JsonConvert.DeserializeObject<RootObject>(test); //test is JSON response as string
foreach (var item in myobject.list)
{
var temp = item.main.temp;
Console.WriteLine(temp);
}
Console.ReadLine();
}
}
}
}
Using
var regexGroups = Regex.Matches(test, "\"main\" ?: ?{ ?\"temp\" ?: ?(\\d+\\.\\d+)")
.Select(x => x.Groups[1].Value);
You can get all \"main\":{\"temp\": values.
//classes.cs
public class RootObject
{
public List<MyList> list { get; set; }
}
public class MyList
{
public Main main { get; set; }
}
public class Main
{
public double temp { get; set; }
}
//API client
using Newtonsoft.Json;
var myobject = JsonConvert.DeserializeObject<RootObject>(test); //test is JSON response as string
foreach(var item in myobject.list){
var temp = item.main.temp;
}
//Hope it helps

Linq to XML complete Child Element XL structure

I'm trying to not use the old way of reading xml and give a try to linq to xml but I'm having a hard time to accomplish the following:
Response XML
<?xml version="1.0" encoding="UTF-8"?>
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/">
<soapenv:Body>
<PolicyResponse>
<serviceResponse>
<responseCode>0</responseCode>
<responseDescription>Success</responseDescription>
<responseDetail>Success</responseDetail>
<paging>
<pageNum>1</pageNum>
<pageSize>3</pageSize>
<totalRecords>3</totalRecords>
</paging>
</serviceResponse>
<detailsResponseList>
<detailResponse>
<policy>
<policySummaryInfo>
<PolicyNumber>1199128</PolicyNumber>
<PremiumChangeAmt>...</PremiumChangeAmt>
<WrittenAmt>...</WrittenAmt>
<PolicyStatusDesc>Expired</PolicyStatusDesc>
<BillingInfo>
<InsuredOrPrincipal>...</InsuredOrPrincipal>
</BillingInfo>
</policySummaryInfo>
</policy>
</detailResponse>
<detailResponse>
<policy>
<policySummaryInfo>
<PolicyNumber>1199128</PolicyNumber>
<PremiumChangeAmt>...</PremiumChangeAmt>
<WrittenAmt>...</WrittenAmt>
<PolicyStatusDesc>Active</PolicyStatusDesc>
<BillingInfo>
<InsuredOrPrincipal>...</InsuredOrPrincipal>
</BillingInfo>
</policySummaryInfo>
</policy>
</detailResponse>
</detailsResponseList>
</PolicyResponse>
</out2:getPolicySummaryResponse>
</soapenv:Body>
</soapenv:Envelope>
I'm doing this in C#
XDocument xdoc = new XDocument();
xdoc = XDocument.Parse(xmlResponse);
IEnumerable<XElement> items =
from el in xdoc.Descendants("detailResponse")
select el;
With this code I'm able to read bot detailReponse element but what I want is to keep only the XML detailResponse structure with the <PolicyStatusDesc></PolicyStatusDesc> equal to Active. Is that possible. I manage to gather specific data based on element names an so but how I can keep the whole XML child element structure (detailResponse element an its childs) by using Linq to XML.
Thanks
In your XML sample there in an invalid
</out2:getPolicySummaryResponse> tag which i have removed
Here is the code you may use for your result
IEnumerable<XElement> items =
from el in xdoc.Descendants("detailResponse")
where el.Element("policy")?.Element("policySummaryInfo")?.Element("PolicyStatusDesc")?.Value == "Active"
select el;
I often use code like below which gives a flatter results than using Serialization:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml;
using System.Xml.Linq;
namespace ConsoleApplication1
{
class Program
{
const string FILENAME = #"c:\temp\test.xml";
static void Main(string[] args)
{
XDocument doc = XDocument.Load(FILENAME);
XElement xPolicyResponse = doc.Descendants("PolicyResponse").FirstOrDefault();
PolicyResponse policyResponse = new PolicyResponse(xPolicyResponse);
}
}
public class PolicyResponse
{
public ServiceResponse serviceResponse { get; set; }
public List<DetailResponse> detailResponse { get; set; }
public PolicyResponse() {}
public PolicyResponse(XElement element)
{
XElement xServiceResponse = element.Element("serviceResponse");
List<XElement> xdetailResponseList = element.Descendants("detailResponse").ToList();
serviceResponse = new ServiceResponse(xServiceResponse);
detailResponse = xdetailResponseList.Select(x => new DetailResponse(x)).ToList();
}
}
public class ServiceResponse
{
public int responseCode { get; set; }
public string responseDescription { get; set; }
public string responseDetail { get; set; }
public int pageNum { get; set; }
public int pageSize { get; set; }
public int totalRecords { get; set; }
public ServiceResponse() { }
public ServiceResponse(XElement element)
{
responseCode = (int)element.Element("responseCode");
responseDescription = (string)element.Element("responseDescription");
responseDetail = (string)element.Element("responseDetail");
pageNum = (int)element.Descendants("pageNum").FirstOrDefault();
pageSize = (int)element.Descendants("pageSize").FirstOrDefault();
totalRecords = (int)element.Descendants("totalRecords").FirstOrDefault();
}
}
public class DetailResponse
{
public string policyNumber { get; set; }
public string premiumChangeAmt { get; set; }
public string writtenAmt { get; set; }
public string policyStatusDesc { get; set; }
public string insuredOrPrincipal { get; set; }
public DetailResponse() { }
public DetailResponse(XElement element)
{
XElement xPolicySummaryInfo = element.Descendants("policySummaryInfo").FirstOrDefault();
policyNumber = (string)xPolicySummaryInfo.Element("PolicyNumber");
premiumChangeAmt = (string)xPolicySummaryInfo.Element("PremiumChangeAmt");
writtenAmt = (string)xPolicySummaryInfo.Element("WrittenAmt");
policyStatusDesc = (string)xPolicySummaryInfo.Element("PolicyStatusDesc");
insuredOrPrincipal = (string)xPolicySummaryInfo.Descendants("InsuredOrPrincipal").FirstOrDefault();
}
}
}
You can get the elements where <PolicyStatusDesc> has the value Active by filtering the <detailResponse> elements using el.Descendants("PolicyStatusDesc"), then selecting those elements having the value Active.
Using query syntax:
var items = from el in xdoc.Descendants("detailResponse")
where el.Descendants("PolicyStatusDesc").Any(p => p.Value == "Active")
select el;
Or using method syntax:
var items = xdoc.Descendants("detailResponse")
.Where(el => el.Descendants("PolicyStatusDesc")
.Any(p => p.Value == "Active"));

How do I deserialize this XML

How correctly to deserialize XML from my example (look the question in the end)? Am I doing this right? Maybe there is a way to make it easier and more efficient?
XML:
<Warehouse>
<GUID>0d63057d-99e8-11e6-813b-0003ff000011</GUID>
<Name>WarehouseName</Name>
<Terms>
<Term TargetGUID="490ecabf-f011-11e3-b7d9-6c626dc1e098">2</Term>
<Term TargetGUID="f332d7ff-efd2-11e3-b7d9-6c626dc1e098">4</Term>
</Terms>
</Warehouse>
C#:
Warehouse.cs:
[Serializable]
public class Warehouse
{
[XmlArray("Terms", IsNullable=true)]
[XmlArrayItem("Term")]
public WarehouseTransferTerm[] TransferTerms { get; set; }
[XmlElement(ElementName="Name")]
public string InternalName { get; set; }
[XmlElement(ElementName="Guid")]
public Guid Guid { get; set; }
}
WarehouseTransferTerm.cs:
[Serializable]
public class WarehouseTransferTerm
{
public Guid SourceWarehouseGuid { get; set; }
[XmlAttribute(AttributeName = "TargetGUID")]
public Guid TargetWarehouseGuid { get; set; }
[XmlElement(ElementName="Term")]
public int TransferTermInDays { get; set; }
}
Question: How I can set Warehouse's GUID property value as SourceWarehouseGuid?
You could implement custom deserialization logic and just set the value on the dependent items. See here: https://learn.microsoft.com/en-us/dotnet/api/system.runtime.serialization.ondeserializedattribute?view=netframework-4.7.2
[OnDeserialized()]
internal void OnDeserializedMethod(StreamingContext context)
{
foreach(var term in TransferTerms)
{
term.TargetWarehouseGuid = this.Guid;
}
}
I like using Dictionaries along with Xml Linq :
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml;
using System.Xml.Linq;
namespace ConsoleApplication1
{
class Program
{
const string FILENAME = #"c:\temp\test.xml";
static void Main(string[] args)
{
XDocument doc = XDocument.Load(FILENAME);
Dictionary<string, XElement> warehouses = doc.Descendants("Warehouse")
.GroupBy(x => (string)x.Element("GUID"), y => y)
.ToDictionary(x => x.Key, y => y.FirstOrDefault());
XElement warehouse = warehouses["0d63057d-99e8-11e6-813b-0003ff000011"];
Dictionary<string, XElement> terms = warehouse.Descendants("Term")
.GroupBy(x => (string)x.Attribute("TargetGUID"), y => y)
.ToDictionary(x => x.Key, y => y.FirstOrDefault());
string value = terms["490ecabf-f011-11e3-b7d9-6c626dc1e098"].Value;
warehouse.SetValue(value);
}
}
}

<string xmlns=''> was not expected in c#

Hi all I am trying to serialize value in xml. Every time I am getting <string xmlns=''> was not expected in c# Not able to find root cause plz help me out here.
namespace CustomDataType.usercontrols
{
public partial class CustomDataTypes : System.Web.UI.UserControl, umbraco.editorControls.userControlGrapper.IUsercontrolDataEditor
{
private Status _umbval;
public object value
{
get
{
var status = GetStatus();
return SerializationHelper.ValueToXmlString(status);
}
set
{
//if (value == null || string.IsNullOrEmpty(value.ToString()))
//{
// _umbval = Status.Empty;
//}
//else
//{
_umbval =(Status)SerializationHelper.ValueFromXmlString(value,typeof(Status));
//}
}
}
}
}
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Xml.Serialization;
namespace CustomDataType
{
[Serializable]
[XmlRoot("StatuMain")]
public class Status
{
[XmlElement("statusvalue")]
public string StatusValue { get; set; }
[XmlElement("statusvalue1")]
public string StatusValue1 { get; set; }
[XmlElement("statusvalue2")]
public string StatusValue2 { get; set; }
[XmlElement("statusvalue3")]
public string StatusValue3 { get; set; }
//[XmlElement("isEmailChecked")]
//public bool HasEmailChecked { get; set; }
//[XmlElement("datetime")]
//public DateTime Date { get; set; }
public static Status Empty
{
get
{
var schedule = new Status();
schedule = null;
return schedule;
}
}
}
}
I think you should be using XmlNamespaceManager to set an empty namespace, instead of xmlns..
http://msdn.microsoft.com/en-us/library/d6730bwt(v=vs.80).aspx
XmlNamespaceManager nsmanager = new XmlNamespaceManager(reader.NameTable);
nsmanager.AddNamespace("", "");
YourSeraializationMethod(reader);

Categories

Resources