i am parsing class with empty string in some element in XML file like below.
objectCxml.Request.InvoiceDetailRequest.InvoiceDetailRequestHeader.InvoiceDetailHeaderIndicator = "";
XmlSerializer s = new XmlSerializer(typeof(cXML));
XmlTextWriter tw = new XmlTextWriter(path, Encoding.UTF8);
s.Serialize(tw, objectCxml);
It generate xml like below
<InvoiceDetailHeaderIndicator xsi:type="xsd:string"/>
But i want it as below
<InvoiceDetailHeaderIndicator/>
Any Suggestion?
InvoiceDetailHeaderIndicator property is object
So... don't do that? Make it string and you should be set.
Ultimately, the point here is that XmlSerializer wants to be able to round-trip data reliably; that is its job. There are two ways of doing that:
know the type statically (i.e. string instead of object in the type model)
embed additional metadata in the payload (xsi:type="xsd:string")
If you don't want 2, you'll need 1, otherwise it can't work. Frankly 1 is a much better idea anyway.
I tested with a local minimal setup, and this worked fine:
public class InvoiceHeaderThing
{
public string InvoiceDetailHeaderIndicator { get; set; }
}
Full code below:
using System;
using System.IO;
using System.Text;
using System.Xml;
using System.Xml.Serialization;
static class P
{
static void Main()
{
const string path = "my.xml";
var objectCxml = new cXML();
objectCxml.Request.InvoiceDetailRequest.InvoiceDetailRequestHeader.InvoiceDetailHeaderIndicator = "";
XmlSerializer s = new XmlSerializer(typeof(cXML));
using (XmlTextWriter tw = new XmlTextWriter(path, Encoding.UTF8))
{
s.Serialize(tw, objectCxml);
}
Console.WriteLine(File.ReadAllText(path));
}
}
public class cXML
{
public RequestThing Request { get; set; } = new RequestThing();
}
public class RequestThing
{
public InvoiceDetailThing InvoiceDetailRequest { get; set; } = new InvoiceDetailThing();
}
public class InvoiceDetailThing
{
public InvoiceHeaderThing InvoiceDetailRequestHeader { get; set; } = new InvoiceHeaderThing();
}
public class InvoiceHeaderThing
{
public string InvoiceDetailHeaderIndicator { get; set; }
}
Related
So I am using TDAmeritrade API to receive stock data with a C# Winforms program on Visual Studio. It takes the user input stock symbol and searches for the info. I am using HttpClient and Newtonsoft.Json and have been able to successfully perform the GET request and receive a JSON string back, but I do not know how to get all of the information I need out of it.
Here is the JSON:
https://drive.google.com/file/d/1TpAUwjyqrHArEXGXMof_K1eQe0hFoaw5/view?usp=sharing
Above is the JSON string sent back to me then formatted. My goal is to record information for each price in "callExpDateMap.2021-02-19:11" and "callExpDateMap.2021-03-19:39". The problem is that for each different stock, the dates that show up in "callExpDateMap" are going to be different.
client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", token);
var response = await client.GetAsync(url);
var info = await response.Content.ReadAsStringAsync();
dynamic config = JsonConvert.DeserializeObject<dynamic>(info, new ExpandoObjectConverter());
return config;
This is the code I have right now. I know the last for statement is not correct. How can I parse to the specific sections I want (callExpDateMap.expirationdate.StrikePrice) and get the information needed from each without knowing the dates and Strike prices beforehand? Is there a way to innumerate it and search through the JSON as if it were all a bunch of arrays?
The code below is perhaps not the most elegant nor complete, but I think it will get you going. I would start by using the JObject.Parse() from the Newtonsoft.Json.Linq namespace and take it from there.
JObject root = JObject.Parse(info);
string symbol = root["symbol"].ToObject<string>();
foreach (JToken toplevel in root["callExpDateMap"].Children())
{
foreach (JToken nextlevel in toplevel.Children())
{
foreach (JToken bottomlevel in nextlevel.Children())
{
foreach (JToken jToken in bottomlevel.Children())
{
JArray jArray = jToken as JArray;
foreach (var arrayElement in jArray)
{
InfoObject infoObject = arrayElement.ToObject<InfoObject>();
Console.WriteLine(infoObject.putCall);
Console.WriteLine(infoObject.exchangeName);
Console.WriteLine(infoObject.multiplier);
}
}
}
}
}
public class InfoObject
{
public string putCall { get; set; }
public string symbol { get; set; }
public string description { get; set; }
public string exchangeName { get; set; }
// ...
public int multiplier { get; set; }
// ...
}
This is official documentation of Newtonsoft method you are trying to use.
https://www.newtonsoft.com/json/help/html/Overload_Newtonsoft_Json_JsonConvert_DeserializeObject.htm
If an API's method returns different json propeties and you cannot trust it's property names all the times, then you can try using a deserialize method that returns .Net object, for example: JsonConvert.DeserializeObject Method (String)
https://www.newtonsoft.com/json/help/html/M_Newtonsoft_Json_JsonConvert_DeserializeObject.htm
That method's signature is this:
public static Object DeserializeObject(string value)
Parameter is: value of type json string.
Return Value is: Object of type object.
If you do not want an Object, then you can of course use a .Net type you have. Such as this method:
JsonConvert.DeserializeObject Method (String)
Any property that you have in both (the .net type and json object) will get populated. If .net type has properties that do not exist in json object, then those will be ignored. If json object has properties that do not exist in.net, then those will be ignored too.
Here's an example of a .Net type
using Microsoft.VisualStudio.TestTools.UnitTesting;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using Newtonsoft.Json.Serialization;
using System.Collections.Generic;
using System.IO;
using System.Text;
namespace MyNameSpace
{
public class TDAmeritradeStockData
{
[JsonProperty("symbol")]
public string Symbol { get; set; }
[JsonProperty("status")]
public string Status { get; set; }
[JsonProperty("callExpDateMap")]
public object CallExpDateMap { get; set; }
//...
//...
public CallExpDateMapType[] CallExpDateMapList { get; set; }
}
public class CallExpDateMapType
{
[JsonProperty("expirationdate")]
public string Expirationdate { get; set; }
[JsonProperty("StrikePrice")]
public List<StrikePriceType> StrikePriceList { get; set; }
}
public class StrikePriceType
{
public string StrikePrice { get; set; }
public List<StrikePricePropertiesType> StrikePricePropertiesList { get; set; }
}
public class StrikePricePropertiesType
{
[JsonProperty("putCall")]
public string PutCall { get; set; }
[JsonProperty("symbol")]
public string Symbol { get; set; }
[JsonProperty("description")]
public string Description { get; set; }
[JsonProperty("exchangeName")]
public string ExchangeName { get; set; }
[JsonProperty("bid")]
public double Bid { get; set; }
[JsonProperty("ask")]
public double Ask { get; set; }
//...
//...
}
[TestClass]
public class TestTestTest
{
[TestMethod]
public void JsonTest()
{
var jsondata = ReadFile("data.json");
var model = JsonConvert.DeserializeObject<TDAmeritradeStockData>(jsondata);
JObject jObject = (JObject)model.CallExpDateMap;
var count = ((JObject)model.CallExpDateMap).Count;
model.CallExpDateMapList = new CallExpDateMapType[count];
var jToken = (JToken)jObject.First;
for (var i = 0; i < count; i++)
{
model.CallExpDateMapList[i] = new CallExpDateMapType
{
Expirationdate = jToken.Path,
StrikePriceList = new List<StrikePriceType>()
};
var nextStrikePrice = jToken.First.First;
while (nextStrikePrice != null)
{
var nextStrikePriceProperties = nextStrikePrice;
var srikePriceList = new StrikePriceType
{
StrikePrice = nextStrikePriceProperties.Path,
StrikePricePropertiesList = JsonConvert.DeserializeObject<List<StrikePricePropertiesType>>(nextStrikePrice.First.ToString())
};
model.CallExpDateMapList[i].StrikePriceList.Add(srikePriceList);
nextStrikePrice = nextStrikePrice.Next;
}
jToken = jToken.Next;
}
Assert.IsNotNull(model);
}
private string ReadFile(string fileName)
{
using (var fileStream = new FileStream(fileName, FileMode.Open, FileAccess.Read))
{
var data = new StringBuilder();
using (var streamReader = new StreamReader(fileStream))
{
while (!streamReader.EndOfStream) data.Append(streamReader.ReadLine());
streamReader.Close();
}
fileStream.Close();
return data.ToString();
}
}
}
}
Hi I have the following Code whereby in my class I have defined all the classes that I will need to build up my JSON string for an eventual put request. I am trying to put my AttributeColor and AttributeSize Fields into that attributes string[] so that the JSON returns the following :
{"group":null,"productid":"42","sku":"211","money":"20.00","categoryid":"42","attributes":["42","green"]}
myObject.attributes = reader["Sizeattribute"].ToString() + reader["ColorAttribute"].ToString();
where am I going wrong? how can I add two fields to this one array for perfect JSON? Right now I am getting the error Cannot implicitly convert type 'string' to 'string[]'
Code Snippet:
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Data.SqlClient;
namespace JSON_Example
{
class Program
{
static void Main(string[] args)
{
// Read From SQL
using (SqlConnection connection = new SqlConnection("Server = localhost;Database=xxxx;UID=admin;PASSWORD=xxxx"))
{
String query = "SELECT * FROM [test].[dbo].[DimProductVariantXXX] "; // Please Change the View that you are pointing towards
using (SqlCommand command = new SqlCommand(query, connection))
{
connection.Open();
List<Product> myObjectList = new List<Product>();
var reader = command.ExecuteReader();
if (reader.HasRows)
{
while (reader.Read())
{
Product myObject = new Product();
myObject.productid = reader["productid"].ToString();
myObject.sku = reader["sku"].ToString();
myObject.money = reader["money"].ToString();
// myObject.categoryid = Convert.ToString(reader["categoryid"]);
myObject.attributes = reader["Sizeattribute"].ToString() + reader["ColorAttribute"].ToString();
// myObject.variantparentid = reader["variantparentid"].ToString();
myObjectList.Add(myObject);
}
}
Console.WriteLine(myObjectList);
Product product = new Product();
String JSONresult = JsonConvert.SerializeObject(product);
string path = #"D:\json\product.json";
if (File.Exists(path))
{
File.Delete(path);
using (var tw = new StreamWriter(path, true))
{
tw.WriteLine(JSONresult.ToString());
tw.Close();
}
}
else if (!File.Exists(path))
{
using (var tw = new StreamWriter(path, true))
{
tw.WriteLine(JSONresult.ToString());
tw.Close();
}
}
}
}
}
}
class Product
{
public Group group;
public string productid { get; set; }
public string sku { get; set; }
public string money { get; set; }
public string categoryid { get; set; }
public string sizeattribute { get; set; }
public string colorattribute { get; set; }
public List<string>[] attributes { get; set; }
}
}
In the Product class, the attributes property should be defined as a single list or array. It's currently defined as an array of List<string> (not sure if this is a typo).
Update the definition:
public List<string> attributes { get; set; }
Now, myObject.attributes is a List<string> so you need to initialise the list and add the values to it.
You could do this using the Collection Initializer:
myObject.attributes = new List<string>
{
reader["Sizeattribute"].ToString(),
reader["ColorAttribute"].ToString()
};
This should produce the expected JSON.
The part at the "invoiceUnit.Items.Add(itemOne);" I get an error. It says:
1. System.NullReferenceException: 'Object reference not set to an
instance of an object.'
2. PROGRAM_B2.Program.Order.Items.get returned null.
Also when I comment out that line of codes, I managed to get it run but the data in invoiceDataOut.xml is nothing. The serializer doesn't seem to be working.
using System;
using System.Collections.Generic;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Xml;
using System.Xml.Serialization;
namespace PROGRAM_B2
{
// This is the class that we want to serialize:.
[Serializable()]
public class Program
{
// Main class which is directly accessed
public class Order
{
public int InvoiceID { get; set; }
private DateTime _InvoiceDate { get; set; }
public string InvoiceDate {
get { return _InvoiceDate.ToString("d/m/yyyy"); }
set { _InvoiceDate = DateTime.ParseExact(value, "d/m/yyyy", CultureInfo.InvariantCulture); }
}
public int SellerID { get; set; }
public int BuyerID { get; set; }
public int OrderID { get; set; }
public List<Item> Items { get; set; }
public double ShippingCharges { get; set; }
public double InvoiceTotalCost { get; set; }
}
// Indirectly accessed class
public class Item
{
public int ItemID { get; set; }
public string ItemName { get; set; }
public string Description { get; set; }
public int Quantity { get; set; }
public double NewUnitPrice { get; set; }
}
static void Main(string[] args)
{
Order invoiceUnit = new Order();
// Simple Type - Main Data
invoiceUnit.InvoiceID = 0011995;
invoiceUnit.InvoiceDate = "12/5/2017";
invoiceUnit.SellerID = 0020;
invoiceUnit.BuyerID = 1231;
invoiceUnit.OrderID = 9021;
// An Item -Multiple
Item itemOne = new Item();
itemOne.ItemID = 0001;
itemOne.ItemName = "Apple Macbook Pro";
itemOne.Description = "The best professional laptop for professionals.";
itemOne.Quantity = 5;
itemOne.NewUnitPrice = 4950.50;
// Add Item
invoiceUnit.Items.Add(itemOne);
// An Item -Multiple
Item itemTwo = new Item();
itemTwo.ItemID = 0002;
itemTwo.ItemName = "Microsoft Surface Laptop";
itemTwo.Description = "The most versatile professional laptop for experts.";
itemTwo.Quantity = 10;
itemTwo.NewUnitPrice = 3500.90;
// Add Item
invoiceUnit.Items.Add(itemTwo);
// Simple Type - Footer Data
invoiceUnit.ShippingCharges = 7000.00;
invoiceUnit.InvoiceTotalCost = 19500.10;
// Create a new XmlSerializer instance with the type of the test class
XmlSerializer SerializerObj = new XmlSerializer(typeof(Program));
// Create a new file stream to write the serialized object to a file
TextWriter WriteFileStream = new StreamWriter(#"../../../invoiceDataOut.xml");
SerializerObj.Serialize(WriteFileStream, invoiceUnit);
// Cleanup
WriteFileStream.Close();
/*
The test.xml file will look like this:
<?xml version="1.0"?>
<TestClass xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<SomeString>foo</SomeString>
<Settings>
<string>A</string>
<string>B</string>
<string>C</string>
</Settings>
</TestClass>
*/
// Test the new loaded object:
/*Console.WriteLine(invoiceUnit.someString);
foreach (string Setting in invoiceUnit.Settings)
{
Console.WriteLine(Setting);
}
Console.ReadLine();
*/
}
}
}
Please let me know how I can make it better.
I have the following xml:
<Applications>
<AccessibleApplication></AccessibleApplication>
<AccessibleApplication></AccessibleApplication>
<EligibleApplication></EligibleApplication>
<EligibleApplication></EligibleApplication>
</Applications>
Is there a way to deserialize this into a C# object so that the AccessibleApplications and EligibleApplications are two separate arrays? I tried the following but get an exception because "Applications" is used more than once.
[XmlArray("Applications")]
[XmlArrayItem("AccessibleApplication")]
public List<Application> AccessibleApplications { get; set; }
[XmlArray("Applications")]
[XmlArrayItem("EligibleApplication")]
public List<Application> EligibleApplications { get; set; }
The exception is:
The XML element 'Applications' from namespace '' is already present in the current scope. Use XML attributes to specify another XML name or namespace for the element.
Is this possible to do?
Thanks!
Edit: I forgot to mention that I do not want to have an "Applications" class just for the sake of providing a container object for the two arrays. I have several situations like this and I don't want the clutter of these classes whose only purpose is to split up two arrays of the same type.
I was hoping to be able to deserialize the two arrays into an outer object using some sort of tag like [XmlArrayItem="Application/AccessibleApplication"] without creating an "Applications" class.
I've found a fairly neat way to do this, first make a class like this:
using System.Xml.Serialization;
[XmlRoot]
public class Applications
{
[XmlElement]
public string[] AccessibleApplication;
[XmlElement]
public string[] EligibleApplication;
}
Notice how the elements are individual arrays. Now using this class (I had my XML in a separate file hence the XmlDocument class).
var doc = new XmlDocument();
doc.Load("../../Apps.xml");
var serializer = new XmlSerializer(typeof(Applications));
Applications result;
using (TextReader reader = new StringReader(doc.InnerXml))
{
result = (Applications)serializer.Deserialize(reader);
}
Now to prove this works you can write this all in to a console app and do a foreach to print all the values in your arrays, like so:
foreach (var app in result.AccessibleApplication)
{
Console.WriteLine(app);
}
foreach (var app in result.EligibleApplication)
{
Console.WriteLine(app);
}
You can use XmlElement attribute to deserialize to different lists:
public class Applications
{
[XmlElement("AccessibleApplication")]
public List<Application> AccessibleApplications { get; set; }
[XmlElement("EligibleApplication")]
public List<Application> EligibleApplications { get; set; }
}
public class Application
{
[XmlText]
public string Value { get; set; }
}
So for a sample XML:
<Applications>
<AccessibleApplication>xyz</AccessibleApplication>
<AccessibleApplication>abc</AccessibleApplication>
<EligibleApplication>def</EligibleApplication>
<EligibleApplication>zzz</EligibleApplication>
</Applications>
The following snippet would output the below:
using (var reader = new StreamReader("XMLFile1.xml"))
{
var serializer = new XmlSerializer(typeof(Applications));
var applications = (Applications)serializer.Deserialize(reader);
Console.WriteLine("AccessibleApplications:");
foreach (var app in applications.AccessibleApplications)
{
Console.WriteLine(app.Value);
}
Console.WriteLine();
Console.WriteLine("EligibleApplications:");
foreach (var app in applications.EligibleApplications)
{
Console.WriteLine(app.Value);
}
}
Output:
AccessibleApplications:
xyz
abc
EligibleApplications:
def
zzz
You can use this class to create objects from strings, creating strings from objects and create byte [] from objects
StringToObject
var applicationObject = new XmlSerializerHelper<Applications>().StringToObject(xmlString);
ObjectToString
var xmlString = new XmlSerializerHelper<Applications>().ObjectToString(applicationObject);
ObjectToByteArray
var byteArray = new XmlSerializerHelper<Applications>().ObjectToByteArray(applicationObject);
The XmlSerializerHelper:
namespace StackOverflow
{
public class XmlSerializerHelper<T> where T : class
{
private readonly XmlSerializer _serializer;
public XmlSerializerHelper()
{
_serializer = new XmlSerializer(typeof(T));
}
public T ToObject(string xml)
{
return (T)_serializer.Deserialize(new StringReader(xml));
}
public string ToString(T obj, string encoding)
{
using (var memoryStream = new MemoryStream())
{
_serializer.Serialize(memoryStream, obj);
return Encoding.GetEncoding(encoding).GetString(memoryStream.ToArray());
}
}
public byte[] ToByteArray(T obj, Encoding encoding = null)
{
var settings = GetSettings(encoding);
using (var memoryStream = new MemoryStream())
{
using (var writer = XmlWriter.Create(memoryStream, settings))
{
_serializer.Serialize(writer, obj);
}
return memoryStream.ToArray();
}
}
private XmlWriterSettings GetSettings(Encoding encoding)
{
return new XmlWriterSettings
{
Encoding = encoding ?? Encoding.GetEncoding("ISO-8859-1"),
Indent = true,
IndentChars = "\t",
NewLineChars = Environment.NewLine,
ConformanceLevel = ConformanceLevel.Document
};
}
}
}
Your Class:
[XmlRoot]
public class Applications
{
[XmlElement("AccessibleApplication")]
public string[] AccessibleApplication { get; set; }
[XmlElement("EligibleApplication")]
public string[] EligibleApplication { get; set; }
}
Or
[XmlRoot]
public class Applications
{
[XmlElement("AccessibleApplication")]
public List<string> AccessibleApplication { get; set; }
[XmlElement("EligibleApplication")]
public List<string> EligibleApplication { get; set; }
}
Cheers.
I have a ArrayList with some user objects in. I am trying to serialize them into an XML format that I can send to a webserver. The format needs to be UTF 8.
I keep running into this error:
The type of the argument object 'User' is not primitive.
This is effectively two issues, however the main one being that this primitive error will not let me try and other web examples for utf8. I simply to not understand why it does this. I have tried using:
[Serializable]
Currently I have a function which will work but it will not do the xml to a utf8 format. And when I try any other examples on the web I then get this primitive error. Below is my current code:
My User Class:
using System;
using System.Xml;
using System.Xml.Serialization;
[Serializable]
public class User
{
public int UserID { get; set; }
public string Name { get; set; }
public string Password { get; set; }
public DateTime DateCreated { get; set; }
public string DeviceMacAddr { get; set; }
public DateTime LastLoggedIn { get; set; }
public float LastLoggedLat { get; set; }
public float LastLoggedLong { get; set; }
public bool Active { get; set; }
public string SyncStatus { get; set; }
public DateTime LastSyncDate { get; set; }
}
My XML writing Script:
using UnityEngine;
using System;
using System.Collections;
using System.Text;
using System.IO;
using System.Xml;
using System.Xml.Serialization;
using System.Collections.Generic;
public class XmlWriting : MonoBehaviour {
private string formatString = "'yyyy'##'MM'##'dd' 'HH'*'mm'*'ss'";
[XmlAttribute("Users")]
ArrayList Users = new ArrayList();
//List<User> Users = new List<User>();
// Use this for initialization
void Start () {
Users.Add(new User { UserID = 1,
Name = "Test Woman",
Password = "aa",
DateCreated = DateTime.Now,
DeviceMacAddr = "24:70:8c:83:86:BD",
LastLoggedIn = DateTime.Now,
LastLoggedLat = 36.083101f,
LastLoggedLong = -11.263433f,
Active = true,
SyncStatus = "Awaiting Response",
LastSyncDate = DateTime.Now,
}
);
Users.Add(new User { UserID = 2,
Name = "Test Man",
Password = "aa",
DateCreated = DateTime.Now,
DeviceMacAddr = "74:21:0c:93:46:XD",
LastLoggedIn = DateTime.Now,
LastLoggedLat = 83.083101f,
LastLoggedLong = -3.261823f,
Active = true,
SyncStatus = "Complete",
LastSyncDate = DateTime.Now,
}
);
var serializer = new XmlSerializer(typeof(ArrayList));
var memoryStream = new MemoryStream();
var streamWriter = new StreamWriter(memoryStream, System.Text.Encoding.UTF8);
serializer.Serialize(streamWriter, Users);
byte[] utf8EncodedXml = memoryStream.ToArray();
Debug.Log ("SerializeArrayList: " );//+ utf8EncodedXml);
}
// Update is called once per frame
void Update () {
}
private string SerializeArrayList(ArrayList obj)
{
XmlDocument doc = new XmlDocument();
XmlSerializer serializer = new XmlSerializer(typeof(ArrayList), new Type[]{typeof(User)});
using (MemoryStream stream = new System.IO.MemoryStream())
{
try
{
serializer.Serialize(stream, obj);
stream.Position = 0;
doc.Load(stream);
Debug.Log ("stream: " + doc.InnerXml);
return doc.InnerXml;
}
catch (Exception ex)
{
}
}
return string.Empty;
}
public class Utf8StringWriter : StringWriter
{
public override Encoding Encoding
{
get { return Encoding.UTF8; }
}
}
}
Any help is much appreciated.
Thanks
You need to put the [Serializable] attribute above the class definitions for User and every other class you intend to serialize. As for UTF-8, it shouldn't matter, at least not logically - C#'s deserializer on the other side should handle any encoding transparently for you.