I am attempting to implement a portion of the Ally Accounts API in C#. I've run into a problem with a particular endpoint.
I want to get a specific account's balance:
[XmlRoot("accountbalance")]
public class SummaryAccountBalance : Balance
{
[XmlElement("buyingpower")]
public BuyingPower BuyingPower { get; set; }
[XmlElement("fedcall")]
public decimal FedCall { get; set; }
[XmlElement("housecall")]
public decimal HouseCall { get; set; }
[XmlElement("money")]
public AccountMoney Money { get; set; }
[XmlElement("securities")]
public Securities Securities { get; set; }
}
Where Balance is a simple abstract class:
public abstract class Balance
{
[XmlElement("account")]
public int Account { get; set; }
[XmlElement("accountvalue")]
public decimal AccountValue { get; set; }
}
For other endpoints this works just fine and I am able to get summary account balance information using this structure, for example in this endpoint.
But the /accounts/xxxxxxx/balances.xml doesn't want to work for me. Given this file:
<?xml version="1.0" encoding="UTF-8"?>
<response id="77cf30da:12df25c7074:-7ea6">
<accountbalance>
<account>12345678</account>
<accountvalue>67119.41</accountvalue>
<buyingpower>
<cashavailableforwithdrawal>66177.48000000001</cashavailableforwithdrawal>
<daytrading>264709.84</daytrading>
<equitypercentage>100</equitypercentage>
<options>66177.48000000001</options>
<soddaytrading>264709.84</soddaytrading>
<sodoptions>66177.48000000001</sodoptions>
<sodstock>132354.96000000002</sodstock>
<stock>132354.96000000002</stock>
</buyingpower>
<fedcall>0.0</fedcall>
<housecall>0.0</housecall>
<money>
<accruedinterest>0.0</accruedinterest>
<cash>66134.67</cash>
<cashavailable>0.0</cashavailable>
<marginbalance>0.0</marginbalance>
<mmf>0.02</mmf>
<total>66134.69</total>
<uncleareddeposits>0.0</uncleareddeposits>
<unsettledfunds>0.0</unsettledfunds>
<yield>0.0</yield>
</money>
<securities>
<longoptions>0.0</longoptions>
<longstocks>57.39</longstocks>
<options>0.0</options>
<shortoptions>0.0</shortoptions>
<shortstocks>0.0</shortstocks>
<stocks>57.39</stocks>
<total>984.72</total>
</securities>
</accountbalance>
</response>
The following code detects it is indeed a SummaryAccountBalance but fails to populate the fields:
var serializer = new XmlSerializer(typeof(SummaryAccountBalance), new XmlRootAttribute("response"));
return (SummaryAccountBalance)serializer.Deserialize(summaryAccountBalance);
where summaryAccountBalance is the byte stream version of the file above.
I'm not sure where I have gone wrong here and I'm pulling my hair out trying to solve it. Even stepping through the code line by line there doesn't seem to be any problems at all until this deserialization step. More to the point, this exact object is used in other endpoints and those endpoint implementations have no problem deserializing it.
What am I missing to make this work?
What am I missing to make this work?
What you are missing is that <response> is not your SummaryAccountBalance. It's the nested <accountbalance> element.
You need to declare one more class to describe the whole XML:
[XmlRoot("response")]
public class SummaryAccountBalanceResponse
{
[XmlAttribute("id")]
public string Id { get; set; }
[XmlElement("accountbalance")]
public SummaryAccountBalance Balance { get; set; }
}
and deserialize into this class:
var serializer = new XmlSerializer(typeof(SummaryAccountBalanceResponse));
return ((SummaryAccountBalanceResponse)serializer.Deserialize(summaryAccountBalance)).Balance;
Related
I need to parse a xml document into object models that I've created but I can't figure out how to do so, I think it's because of my lack of understanding of the xml structure.
I've tried to get all the elements from the document and to create individual object from each based on their attributes I think they're called.
Here is my C# code :
var manifest = XDocument.Load(theDocument);
var allTheElements = manifest.Descendants();
foreach (var element in allTheElements)
{
//No idea how to parse each object into individual ManifestModel's
}
public class ManifestModel
{
public string Version { get; set; }
public string Resource { get; set; }
public string Size { get; set; }
public string Checksum { get; set; }
}
And here is the XML data :
<?xml version="1.0" encoding="UTF-8"?>
<manifest version="1.0.0" totalbytes="6131797">
<source uri="codeapi.io/Game/patches/">
<file resource="FooGame.sln" size="1125" checksum="530B9F1C2412A6D74EF017919074FD8966E5280D" />
<file resource=".vs\FooGame\v16\.suo" size="69120" checksum="438976A3681FDD503DB4FBFCBB5D420E9E8838DD" />
</source>
</manifest>
Just like we have json2csharp for JSON, we have Xml2Csharp for XML. There are probably lots of other sites that will do this.
Paste your XML and it generates this:
[XmlRoot(ElementName="file")]
public class File {
[XmlAttribute(AttributeName="resource")]
public string Resource { get; set; }
[XmlAttribute(AttributeName="size")]
public string Size { get; set; }
[XmlAttribute(AttributeName="checksum")]
public string Checksum { get; set; }
}
[XmlRoot(ElementName="source")]
public class Source {
[XmlElement(ElementName="file")]
public List<File> File { get; set; }
[XmlAttribute(AttributeName="uri")]
public string Uri { get; set; }
}
[XmlRoot(ElementName="manifest")]
public class Manifest {
[XmlElement(ElementName="source")]
public Source Source { get; set; }
[XmlAttribute(AttributeName="version")]
public string Version { get; set; }
[XmlAttribute(AttributeName="totalbytes")]
public string Totalbytes { get; set; }
}
One could call that lazy or cheating, but I don't see the point in writing code that can be generated for me in a second. You might not always get perfect results, but it's a good starting point. For example, it uses string for all attribute types. If you're expecting all numeric values you could replace those with int or long.
Now you can deserialize like this:
var serializer = new XmlSerializer(typeof(Manifest), new XmlRootAttribute("manifest"));
using (var stream = System.IO.File.OpenRead("test.xml"))
{
var deserialized = (Manifest)serializer.Deserialize(stream);
}
Once you've got the data deserialized into something, the rest is much easier. You can either use the auto-generated models or map them to your own.
Using LINQ...
c#
void Main()
{
string fileName = #"e:\Temp\GamePatches.xml";
XDocument manifest = XDocument.Load(fileName);
string version = manifest.Root.Attribute("version").Value;
List<ManifestModel> manifestModel = new List<ManifestModel>();
foreach (XElement e in manifest.Descendants("file"))
{
manifestModel.Add(new ManifestModel() { Version = version
, Resource = (string)e.Attribute("resource").Value
, Size = (string)e.Attribute("size").Value
, Checksum = (string)e.Attribute("checksum").Value }
);
}
}
// Define other methods and classes here
public class ManifestModel
{
public string Version { get; set; }
public string Resource { get; set; }
public string Size { get; set; }
public string Checksum { get; set; }
}
I spent a lot of time working on a similar app that parsed through XML Schema, and I found the easiest way is to turn the XML Document into an XmlNodeList. From here you can use the SelectNodes and SelectSingleNodes to navigate through it. Take a look at this:https://learn.microsoft.com/en-us/dotnet/api/system.xml.xmlnode.selectnodes?view=netframework-4.8, but basically what you do is create an xpath string which selects the node you need. Here is some documentation on that: https://learn.microsoft.com/en-us/dotnet/standard/data/xml/select-nodes-using-xpath-navigation
I have a JSON string and I need some help to deserialize it.
At the moment my result is always null.
var results = JsonConvert.DeserializeObject<Root>(json);
// result == null
My JSON:
{"First":{"FirstData1":{"date":"2018-01-01","hint":""},
"FirstData2":{"date":"2018-01-06","hint":""}},
"Second":{"SecondData1":{"date":"2018-01-01","hint":""},
"SecondData2":{"date":"2018-01-06","hint":""}}}....
Only on the last Node there is actual property naming...
MyObjects
public class Root
{
public IEnumerable<TempModelRoot> Value{ get; set; }
}
public class TempModelRoot
{
[JsonProperty("Key")]
public string Key { get; set; }
[JsonProperty("Value")]
public List<TempModelChild> Value { get; set; }
}
public class TempModelChild
{
[JsonProperty("Key")]
public string Key { get; set; }
[JsonProperty("Value")]
public TempModelInfo Value { get; set; }
}
public class TempModelInfo
{
[JsonProperty("date")]
public string date { get; set; }
[JsonProperty("hint")]
public string hint { get; set; }
}
In addition to #MX D 's answer, I want to add two more useful model generator sites, which takes JSON as an input and gives appropriate model classes.
Json2Csahrp
JsonUtils
Use, whenever you find difficult to generate complex model classes.
Most likely you are having a mismatch between the model you are trying to deserialize to, and the actual expected model based of the json itself.
A easy way to resolve this is by using a tool such as Quick Types Model Generator(unafiliated) which allows you to generate C# models based upon a provided json file.
After generation you can compare and/or replace your models with the generated models.
To spot and resolve the issue with your model.
My XML:
<result>
<document version="2.1.0">
<response type="currency">
<currency>
<code>AMD</code>
<price>85.1366</price>
</currency>
</response>
<response type="currency">
<currency>
<code>AUD</code>
<price>31.1207</price>
</currency>
</response>
</document>
</result>
My Class:
public class CurrencyData
{
public string Code { get; set; }
public string Price { get; set; }
}
My deserializer calling:
RestClient.ExecuteAsync<List<CurrencyData>>...
If i renamed class CurrencyData to Currency then all will been done right. But I want to keep this class name.
Ok, I think I got it,
You can try RestClient.ExecuteAsync<Result>()
[XmlRoot("result")]
public class Result
{
[XmlElement("document")]
public Document Document { get; set; }
}
public class Document
{
[XmlElement("response")]
public Response[] Responses { get; set; }
[XmlAttribute("version")]
public string Version { get; set; }
}
public class Response
{
[XmlElement("currency")]
public CurrencyData Currency { get; set; }
[XmlAttribute("type")]
public string Type { get; set; }
}
public class CurrencyData
{
[XmlElement("code")]
public string Code { get; set; }
[XmlElement("price")]
public decimal Price { get; set; }
}
I had to add a few XmlElement attribute to override the casing without having to name classes and properties in lowercase. but you can drop them if you can change the xml to match the casing
Answer of kay.one is perfect! It works with a any remarks:
public List<Response> Responses { get; set; }
works
public Response[] Responses { get; set; }
don`t works
And
[XmlElement("AnyValue")]
it is from System.Xml.Serialization namespace and don`t work.
Feel free to just delete them.
In this example annotation attributes and properties has same names and serializer understands.
But right annotation attributes are from RestSharp.Deserializers namespace
[DeserializeAs(Name="AnyXmlValue")]
public string AnyModelValue { get; set; }
How to manage deserialization of RestSharp
Then change the xml tag to CurrencyData. Here is the documentation about the xml deserializer: https://github.com/restsharp/RestSharp/wiki/Deserialization
I'm not sure why Kay.one's answer is accepted, it doesn't answer the question.
Per my comment, the default RestSharp deserializer doesn't check for the DeserializeAs attribute when deserializing a list. I'm not sure if that's intentional or a mistake as the author doesn't seem to be very available.
Anyways it's a simple fix.
private object HandleListDerivative(object x, XElement root, string propName, Type type)
{
Type t;
if (type.IsGenericType)
{
t = type.GetGenericArguments()[0];
}
else
{
t = type.BaseType.GetGenericArguments()[0];
}
var list = (IList)Activator.CreateInstance(type);
var elements = root.Descendants(t.Name.AsNamespaced(Namespace));
var name = t.Name;
//add the following
var attribute = t.GetAttribute<DeserializeAsAttribute>();
if (attribute != null)
name = attribute.Name;
I am having trouble using the 'Paste XML as Classes' feature in VS2012 to properly deserialize XML results from a Rest call using Web API.
The XML response from the call looks like this:
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<SCResponse>
<AccountId>86</AccountId>
<Administrator>false</Administrator>
<Email>6z#z.com</Email>
<FirstName>6z#z.com</FirstName>
<Label>false</Label>
<LastName>6z#z.com</LastName>
<link href="https://cnn.com" rel="news" title="News"/>
</SCResponse>
I copied this XML and used the handy new feature to paste this XML as classes:
namespace Models.account.response
{
[XmlRoot(ElementName = "SCResponse")] // I added this so I could name the object Account
[DataContract(Name = "SCResponse", Namespace = "")] // I added this as the namespace was causing me problems
public partial class Account
{
public byte AccountId { get; set; }
public bool Administrator { get; set; }
public string Email { get; set; }
public string FirstName { get; set; }
public bool Label { get; set; }
public string LastName { get; set; }
[XmlElement("link")]
public SCResponseLink[] Link { get; set; }
}
[XmlType(AnonymousType = true)]
public partial class SCResponseLink
{
private string hrefField;
private string relField;
private string titleField;
[XmlAttribute)]
public string href { get; set; }
XmlAttribute]
public string rel { get; set; }
[XmlAttribute]
public string title { get; set; }
}
}
}
I call the REST endpoint like so:
string path = String.Format("account/{0}", id);
HttpResponseMessage response = client.GetAsync(path).Result; // Blocking call!
if (response.IsSuccessStatusCode)
{
// Parse the response body. Blocking!
account = response.Content.ReadAsAsync<Models.account.response.Account>().Result;
}
and examine the fields of the Account object -- all are null or defaulting to initialized values.
In my Global.asax.cs Application_Start method, I am registering the XML Serializer:
GlobalConfiguration.Configuration.Formatters.XmlFormatter.UseXmlSerializer = true;
A simpler way to handle this might be to use the RestSharp library, which will do all of the deserialization for you. This will simplify your calls, and you won't need the XML attributes on your model.
Take a look here for a good example of doing aync calls with RestSharp:
How should I implement ExecuteAsync with RestSharp on Windows Phone 7?
Hopefully this helps.
I have some XML with an element like this:
<hour base_forecast="12" datim="29/0" />
And am receiving the error:
Unexpected node type Element. ReadElementString method can only be
called on elements with simple or empty content.
I am guessing this is because the element has no value. I don't control this XML so I can't change it. How would I deserialize this?
** EDIT **
One of the attributes' values is ">6" .... could this be the culprit? If so, how do I handle that?
** Update **
Found some data that wasn't returning a > in a value of the attribute. Same error is occurring.
** Edit #3 *
Created an XSD for the XML I am receiving, then generated classes for them with the xsd tool. Adding to the bottom of this post.
Here is the Deserialization code:
HttpWebRequest req = (HttpWebRequest)WebRequest.Create("xxx");
HttpWebResponse resp = (HttpWebResponse)req.GetResponse();
WeatherData Result = new WeatherData();
using (Stream st = resp.GetResponseStream())
{
XmlRootAttribute xRoot = new XmlRootAttribute();
xRoot.ElementName = "model_data";
xRoot.IsNullable = true;
Result = new XmlSerializer(typeof(WeatherData), xRoot).Deserialize(st) as WeatherData; ** Error here
Xml returned:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE observation SYSTEM "http://private.com/hithere.dtd">
<model_data>
<site a="28/12" b="KXXX">
<hour x="-9999" y="-9999" z="-9999"/>
</site>
</model_data>
Data object
[Serializable, XmlRoot("model_data")]
public class WeatherData
{
[XmlElement("site")]
public string City { get; set; }
[XmlAttribute]
public string a { get; set; }
[XmlAttribute]
public string b { get; set; }
[XmlElement(ElementName="hour", IsNullable=true)]
public string Hour { get; set; }
[XmlAttribute]
public string x { get; set; }
[XmlAttribute]
public string y { get; set; }
[XmlAttribute]
public string z { get; set; }
}
XSD Tool generated classes
**Removed generated classes, but they are similar to what Hugo posted **
Looking at this part:
[XmlElement("site")]
public string City { get; set; }
<Site> contains <Hour>, so it is not an element with simple or empty content, I guess?
Edit: actually the whole thing seems suspect.
The data object seems to disregard all hierarchy information in the xml.
How about something like this?
[Serializable, XmlRoot("model_data")]
public class WeatherData
{
[XmlElement("site")]
public City[] City { get; set; }
}
public class City
{
[XmlAttribute]
public string a { get; set; }
[XmlAttribute]
public string b { get; set; }
[XmlElement(ElementName="hour", IsNullable=true)]
public Hour Hour { get; set; }
}
public class Hour
{
[XmlAttribute]
public string x { get; set; }
[XmlAttribute]
public string y { get; set; }
[XmlAttribute]
public string z { get; set; }
}
When I generated the classes with the XSD tool, I getting the same error, but the error was showing being thrown from a line I had commented out.
So I exited VS, and ran disk cleanup. Ran my code again. Received a message "For security reasons DTD is prohibited in this XML document. etc." So, I allowed set the reader to DtdProcessing.Parse, and ran the code once more.
It was successful.