Serialize XML without tag name - c#

I have the following XML format:-
<?xml version="1.0"?>
<Price xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<amount>
<currency>USD</currency>
100
</amount>
<amount>
<currency>EUR</currency>
50
</amount>
</Price>
the XML value contains the amount in the xml root. May I know how can I serialize the value of 100 & 50 ?
[Serializable]
[XmlRoot("amount")]
public sealed class amount
{
[XmlElement("currency")]
public string currency{ get; set; }
}
class Program
{
static void Main(string[] args)
{
var list = new List<amount> {new amount() {Description = "USD"}, new amount() {Description = "EUR"}};
var serializer = new XmlSerializer(typeof(List<amount>), new XmlRootAttribute("Price"));
var ms = new MemoryStream();
serializer.Serialize(ms, list);
ms.Position = 0;
var result = new StreamReader(ms).ReadToEnd();
}
}

You can use XmlText:
[XmlRoot("amount")]
public sealed class amount
{
[XmlElement("currency")]
public string Description { get; set; }
// http://stackoverflow.com/a/1528429/613130
[XmlIgnore]
public int Value { get; set; }
[XmlText]
[Browsable(false), EditorBrowsable(EditorBrowsableState.Never)]
public string ValueXml
{
get
{
return XmlConvert.ToString(Value);
}
set
{
Value = XmlConvert.ToInt32(value);
}
}
}

Related

Serialize property of custom type as a string

There is a simple Product class. Pay attention to the PriceCur property the rest is unimportant:
public class Product
{
public string? Name { get; set; }
public decimal Price { get; set; }
public Currency PriceCur { get; set; }
public string ToXml()
{
XmlSerializer serializer = new(typeof(Product));
using MemoryStream stream = new();
using StreamWriter writer = new(stream, Encoding.UTF8);
using XmlWriter xmlWriter = XmlWriter.Create(writer, new XmlWriterSettings() { Indent = true });
serializer.Serialize(xmlWriter, this);
return Encoding.UTF8.GetString(stream.ToArray());
}
}
This is a Currency struct
public struct Currency
{
static readonly Dictionary<string, string> currencies = new();
static Currency()
{
currencies.Add("USD", "USD");
currencies.Add("$", "USD");
currencies.Add("$", "USD");
currencies.Add("EUR", "EUR");
currencies.Add("€", "EUR");
currencies.Add("€", "EUR");
}
public Currency(string currency)
{
Value = currencies.ContainsKey(currency) ? currencies[currency] : null;
}
public string? Value { get; set; }
}
Now I serialize Product
Product p = new()
{
Name = "Chocolate",
Price = 5,
PriceCur = new("$")
};
File.WriteAllText("product.xml", p.ToXml());
... and get XML like this:
<Product>
<Name>Chocolate</Name>
<Price>5</Price>
<PriceCur>
<Value>USD</Value>
</PriceCur>
</Product>
What should I do to make nested tags PriceCur\Value\USD into just PriceCur\USD like this:
<Product>
<Name>Chocolate</Name>
<Price>5</Price>
<PriceCur>USD</PriceCur>
</Product>

C# class to XML where a list of entries show up without "father property" node

Having the following example code:
public class MyHeader
{
public string Identification { get; set; }
public DateTime Date { get; set; }
public string Description { get; set; }
public List<MyChild> Children { get; set; }
}
public class MyChild
{
public string Identification { get; set; }
public string Description { get; set; }
}
public string GetXML()
{
var h = new MyHeader
{
Date = DateTime.Now,
Description = "Something",
Identification = "AAAAA11111",
Children = new List<MyChild>
{
new MyChild { Identification = "B1", Description = "B1"},
new MyChild { Identification = "B2", Description = "B2"},
new MyChild { Identification = "B3", Description = "B3"}
}
};
var xml = "";
var serializer = new XmlSerializer(h.GetType());
using (var sww = new StringWriter())
{
using (XmlWriter writer = XmlWriter.Create(sww))
{
serializer.Serialize(writer, h);
xml = sww.ToString();
}
}
return xml;
}
The result is:
<?xml version="1.0" encoding="utf-16"?>
<MyHeader xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<Identification>AAAAA11111</Identification>
<Date>2020-09-09T11:55:04.2457349+02:00</Date>
<Description>Something</Description>
<Children>
<MyChild>
<Identification>B1</Identification>
<Description>B1</Description>
</MyChild>
<MyChild>
<Identification>B2</Identification>
<Description>B2</Description>
</MyChild>
<MyChild>
<Identification>B3</Identification>
<Description>B3</Description>
</MyChild>
</Children>
</MyHeader>
Now the question: what is the easiest solution to produce the final XML without showing the <Children> node, but merging the children in the Header?
Result that I am looking for:
<?xml version="1.0" encoding="utf-16"?>
<MyHeader xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<Identification>AAAAA11111</Identification>
<Date>2020-09-09T11:55:04.2457349+02:00</Date>
<Description>Something</Description>
<MyChild>
<Identification>B1</Identification>
<Description>B1</Description>
</MyChild>
<MyChild>
<Identification>B2</Identification>
<Description>B2</Description>
</MyChild>
<MyChild>
<Identification>B3</Identification>
<Description>B3</Description>
</MyChild>
</MyHeader>
Add an XmlElementAttribute above the list declaration:
[XmlElement(ElementName = "MyChild")]
public List<MyChild> Children { get; set; }
This will treat each child as its own item in the parent MyHeader object.
Example

how to send integer type data in xml

I am creating XML to send data. The data contains multiple datatypes as string, integer and decimal. My XML format and c# code to create it as follows.
<root>
<data>
<struct>
<PartnerCD></PartnerCD>
<UserName> </UserName>
<Password> </Password>
<Action> </Action>
<OfficeCD></OfficeCD>
<ChannelCD></ChannelCD>
<Token></Token>
<Notes> </Notes>
<Products>
<Product>
<ProductID></ProductID>
<SVA></SVA>
<Amount></Amount>
</Product>
</Products>
</struct>
</data>
</root>
And my c# code is
public static string CreateRequestXML(string partnerCd, string userName, string password, string action, string productId, string token, string sva, string amount)
{
XmlDocument doc = new XmlDocument();
XmlElement elemRoot = doc.CreateElement("root");
XmlElement elemData = doc.CreateElement("data");
XmlElement elemStruct = doc.CreateElement("struct");
XmlElement elemProducts = doc.CreateElement("Products");
XmlElement elemProduct = doc.CreateElement("Product");
doc.AppendChild(elemRoot);
elemRoot.AppendChild(elemData);
elemData.AppendChild(elemStruct);
//Insert data here
InsertDataNode(doc, elemStruct, "PartnerCD", partnerCd);
InsertDataNode(doc, elemStruct, "UserName", userName);
InsertDataNode(doc, elemStruct, "Password", password);
InsertDataNode(doc, elemStruct, "Action", action);
InsertDataNode(doc, elemStruct, "Token", token);
elemStruct.AppendChild(elemProducts);
elemProducts.AppendChild(elemProduct);
InsertDataNode(doc, elemProduct, "ProductID", productId);
InsertDataNode(doc, elemProduct, "SVA", sva);
InsertDataNode(doc, elemProduct, "Amount", amount);
return doc.OuterXml;
}
private static void InsertDataNode(XmlDocument doc, XmlElement parentElem, string nodeName, string nodeValue)
{
XmlElement elem = doc.CreateElement(nodeName);
elem.InnerText = nodeValue;
parentElem.AppendChild(elem);
}
and am getting the result as
<root>
<data>
<struct>
<PartnerCD>123</PartnerCD>
<UserName>api</UserName>
<Password>pass</Password>
<Action>token</Action>
<Token>4847898</Token>
<Products>
<Product>
<ProductID>123</ProductID>
<SVA>e8a8227c-bba3-4f32-a2cd-15e8f246344b</SVA>
<Amount>700</Amount>
</Product>
</Products>
</struct>
</data>
</root>
I want the PartnerCD and ProductId elements as integer and Amount element as decimal. I have tried XMLNodeType but no use.
Well the data type is irrelevant in the XML as such, it's all defined in the schema it uses, that's where the declaration of expected data types are stored.
So if you have a XSD that says that /root/data/struct/PartnerCD is of type xs:int then you will get validation errors upon validating the file. The XML itself is just a container of all data, it doesn't include the meta information. You CAN define it manually, but the Point is beyond me in about 99.99% of the cases, and it's more or less a bastard of XML, like MSXML that will understand what you're up to.
Xml has no concept of "data type" natively, the XmlNodeType you refer to is what type of Node it is (such as element, attribute etc), not what type of data is contained within it.
Personally I would create an object and Serialize\Deserialize to\from XML like so.....
using System;
using System.Xml.Serialization;
using System.Collections.Generic;
using System.IO;
namespace _37321906
{
class Program
{
static void Main(string[] args)
{
Root root = new Root();
root.Data.Struct.PartnerCD = 123;
root.Data.Struct.UserName = "api";
root.Data.Struct.Password = "pass";
root.Data.Struct.Action = "token";
root.Data.Struct.Token = 4847898;
root.Data.Struct.Products.Product.Add(new Product { ProductID = 123, SVA = "e8a8227c-bba3-4f32-a2cd-15e8f246344b", Amount = 700.0001 });
// Serialize the root object to XML
Serialize<Root>(root);
// Deserialize from XML
Root DeserializeRoot = Deserialize<Root>();
}
private static void Serialize<T>(T data)
{
// Use a file stream here.
using (TextWriter WriteFileStream = new StreamWriter("test.xml"))
{
// Construct a SoapFormatter and use it
// to serialize the data to the stream.
XmlSerializer SerializerObj = new XmlSerializer(typeof(T));
try
{
// Serialize EmployeeList to the file stream
SerializerObj.Serialize(WriteFileStream, data);
}
catch (Exception ex)
{
Console.WriteLine(string.Format("Failed to serialize. Reason: {0}", ex.Message));
}
}
}
private static T Deserialize<T>() where T : new()
{
//List<Employee> EmployeeList2 = new List<Employee>();
// Create an instance of T
T ReturnListOfT = CreateInstance<T>();
// Create a new file stream for reading the XML file
using (FileStream ReadFileStream = new FileStream("test.xml", FileMode.Open, FileAccess.Read, FileShare.Read))
{
// Construct a XmlSerializer and use it
// to serialize the data from the stream.
XmlSerializer SerializerObj = new XmlSerializer(typeof(T));
try
{
// Deserialize the hashtable from the file
ReturnListOfT = (T)SerializerObj.Deserialize(ReadFileStream);
}
catch (Exception ex)
{
Console.WriteLine(string.Format("Failed to serialize. Reason: {0}", ex.Message));
}
}
// return the Deserialized data.
return ReturnListOfT;
}
// function to create instance of T
public static T CreateInstance<T>() where T : new()
{
return (T)Activator.CreateInstance(typeof(T));
}
}
[XmlRoot(ElementName = "Product")]
public class Product
{
[XmlElement(ElementName = "ProductID")]
public int ProductID { get; set; }
[XmlElement(ElementName = "SVA")]
public string SVA { get; set; }
[XmlElement(ElementName = "Amount")]
public double Amount { get; set; }
}
[XmlRoot(ElementName = "Products")]
public class Products
{
public Products()
{
this.Product = new List<Product>();
}
[XmlElement(ElementName = "Product")]
public List<Product> Product { get; set; }
}
[XmlRoot(ElementName = "struct")]
public class Struct
{
public Struct()
{
this.Products = new Products();
}
[XmlElement(ElementName = "PartnerCD")]
public int PartnerCD { get; set; }
[XmlElement(ElementName = "UserName")]
public string UserName { get; set; }
[XmlElement(ElementName = "Password")]
public string Password { get; set; }
[XmlElement(ElementName = "Action")]
public string Action { get; set; }
[XmlElement(ElementName = "OfficeCD")]
public string OfficeCD { get; set; }
[XmlElement(ElementName = "ChannelCD")]
public string ChannelCD { get; set; }
[XmlElement(ElementName = "Token")]
public int Token { get; set; }
[XmlElement(ElementName = "Notes")]
public string Notes { get; set; }
[XmlElement(ElementName = "Products")]
public Products Products { get; set; }
}
[XmlRoot(ElementName = "data")]
public class Data
{
public Data()
{
this.Struct = new Struct();
}
[XmlElement(ElementName = "struct")]
public Struct Struct { get; set; }
}
[XmlRoot(ElementName = "root")]
public class Root
{
public Root()
{
this.Data = new Data();
}
[XmlElement(ElementName = "data")]
public Data Data { get; set; }
}
}
find your XML in the build folder called test.xml

Custom XML feed based on .Net MVC model

We're redoing a legacy application using .Net MVC 5. Part of the application generates an XML feed which other applications use.
We've built a model called Call
[XmlRoot("record")]
public class Call
{
[XmlIgnore]
public int ID { get; set; }
[XmlIgnore]
public string CustomerInitials { get; set; }
[XmlIgnore]
public string Prefix { get; set; }
[XmlIgnore]
public string Code { get; set; }
[XmlIgnore]
public string CustomerNumber { get; set; }
[XmlElement("starttime")]
public DateTime Entry { get; set; }
[XmlElement("endtime")]
public DateTime Exit { get; set; }
[XmlElement("cnumber")][NotMapped]
public string Number
{
get { return Prefix + Code + CustomerNumber; }
}
}
With a controller:
public class CallsController : ApiController
{
private DbContext db = new DbContext();
// GET: api/Calls
public IQueryable<Call> GetCalls()
{
return db.Calls;
}
}
Which produces an XML feed of:
<ArrayOfCall xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<Call>
<starttime>2016-01-15T14:45:24.447</starttime>
<endtime>2016-01-15T15:45:24.447</endtime>
</Call>
<Call>
<starttime>2016-01-15T15:46:35.637</starttime>
<endtime>2016-01-15T16:46:35.637</endtime>
</Call>
</ArrayOfCall>
However, I need to customize the XML feed to provide the following output:
<?xml version="1.0" encoding="UTF-8"?>
<DATA>
<RECORD>
<CNUMBER>593042401</CNUMBER>
<STARTTIME>2016-01-15T14:45:24.447</STARTTIME>
<ENDTIME>2016-01-15T15:45:24.447</ENDTIME>
</RECORD>
<RECORD>
<CNUMBER>593042401</CNUMBER>
<STARTTIME>2016-01-15T15:46:35.637</STARTTIME>
<ENDTIME>2016-01-15T16:46:35.637</ENDTIME>
</RECORD>
</DATA>
The XmlRoot data annotation is not followed and the CNumber is not included in my XML feed. The calls should display as <record> in the XML feed with the root being <data>. How do I resolve these issues? Am I going about this the wrong way? Should I be using a View Model instead?
1.- The Data class:
using System;
using System.Collections.Generic;
using System.Xml.Serialization;
namespace StackOverflow
{
public class Call
{
[XmlIgnore]
public int ID { get; set; }
[XmlIgnore]
public string CustomerInitials { get; set; }
[XmlIgnore]
public string Prefix { get; set; }
[XmlIgnore]
public string Code { get; set; }
[XmlIgnore]
public string CustomerNumber { get; set; }
[XmlElement("CNUMBER")]
public string Number
{
get { return Prefix + Code + CustomerNumber; }
}
[XmlElement("STARTTIME")]
public DateTime Entry { get; set; }
[XmlElement("ENDTIME")]
public DateTime Exit { get; set; }
}
[XmlRoot(ElementName = "DATA")]
public class Data
{
[XmlElement("RECORD")]
public List<Call> Calls;
}
}
2.- Using your example data:
// 1.- Data
var data = new Data();
var calls = new List<Call>
{
new Call
{
Entry = new DateTime(2016, 1, 15, 14, 45, 24, 447),
Exit = new DateTime(2016, 1, 15, 15, 45, 24, 447)
},
new Call
{
Entry = new DateTime(2016, 1, 15, 15, 46, 35, 637),
Exit = new DateTime(2016, 1, 15, 16, 46, 35, 637)
}
};
data.Calls = new List<Call>(calls);
// 2.- Serialize the objet to byte[]
var dataByteArray = new XmlSerializerHelper<Data>().ObjectToByteArray(data, Encoding.GetEncoding("UTF-8"), true);
// 3.- Save the byte[] to disk
File.WriteAllBytes("D:/xml.xml", dataByteArray);
3.- The serializer helper:
using System;
using System.IO;
using System.Text;
using System.Xml;
using System.Xml.Serialization;
namespace StackOverflow
{
public class XmlSerializerHelper<T> where T : class
{
private readonly XmlSerializer _serializer;
public XmlSerializerHelper()
{
_serializer = new XmlSerializer(typeof(T));
}
public byte[] ObjectToByteArray(T obj, Encoding encoding = null, bool ignoreNAmespaces = false)
{
var settings = GetSettings(encoding);
using (var memoryStream = new MemoryStream())
{
using (var writer = XmlWriter.Create(memoryStream, settings))
{
if (ignoreNAmespaces)
{
var serializerNamespaces = new XmlSerializerNamespaces();
serializerNamespaces.Add("", "");
_serializer.Serialize(writer, obj, serializerNamespaces);
}
else
{
_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
};
}
}
}
4.- Output:
<?xml version="1.0" encoding="utf-8"?>
<DATA>
<RECORD>
<STARTTIME>2016-01-15T14:45:24.447</STARTTIME>
<ENDTIME>2016-01-15T15:45:24.447</ENDTIME>
</RECORD>
<RECORD>
<STARTTIME>2016-01-15T15:46:35.637</STARTTIME>
<ENDTIME>2016-01-15T16:46:35.637</ENDTIME>
</RECORD>
</DATA>
Cheers
To create XML that would match the what you are wanting you could create an Object named Data which could provide a list that would hold Records.
public class Data{
public List<Record> Records{get;set;}
}
Currently you are returning an array of Calls.

XmlSerializer add attribute

I have this item Class :
public class Movie
{
public string VideoId { get; set; }
public string Title { get; set; }
}
And i have List<Movie> of this items and i use this code to Serialize to xml file:
string fileName = index + ".xml";
string serializationFile = Path.Combine(dir, fileName);
XmlWriterSettings settings = new XmlWriterSettings();
settings.Indent = true;
using (var writer = XmlWriter.Create(serializationFile, settings))
{
var serializer = new XmlSerializer(typeof(List<Movie>));
serializer.Serialize(writer, tmpList);
}
And this is the result:
<?xml version="1.0" encoding="utf-8"?>
<ArrayOfMovie xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<Movie>
<VideoId>MyId</VideoId>
<Title>MyTitle</Title>
</Movie>
<Movie>
<VideoId>MyId1</VideoId>
<Title>MyTitle1</Title>
</Movie>
<Movie>
<VideoId>MyId2</VideoId>
<Title>MyTitle2</Title>
</Movie>
<Movie>
<VideoId>MyId3</VideoId>
<Title>MyTitle3</Title>
</Movie>
</ArrayOfMovie>
And it this possible to add attribute to the ArrayOfMovie node,something like this:
<ArrayOfMovie xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" customattribute='Yes'>
Yes, you can do this using the XmlAttribute attribute. In order to do this, you need to define your custom attribute. It comes with the price of one more class that represents the array (nested in the root node). If you have no problem with this addition, then the solution can look like this:
public class ArrayOfMovie
{
// define the custom attribute
[XmlAttribute(AttributeName="CustomAttribute")]
public String Custom { get; set; }
// define the collection description
[XmlArray(ElementName="Items")]
public List<Movie> Items { get; set; }
}
public class Movie
{
public string VideoId { get; set; }
public string Title { get; set; }
}
Then create, fill and serialize as you already do - the one new thing is to fill your custom attribute:
// create and fill the list
var tmpList = new List<Movie>();
tmpList.Add(new Movie { VideoId = "1", Title = "Movie 1" });
tmpList.Add(new Movie { VideoId = "2", Title = "Movie 2" });
// create the collection
var movies = new ArrayOfMovie
{
Items = tmpList,
Custom = "yes" // fill the custom attribute
};
// serialize
using (var writer = XmlWriter.Create(serializationFile, settings))
{
var serializer = new XmlSerializer(typeof(ArrayOfMovie));
serializer.Serialize(writer, movies);
}
The XML output looks like this:
<?xml version="1.0" encoding="utf-8"?>
<ArrayOfMovie xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
CustomAttribute="yes">
<Items>
<Movie>
<VideoId>1</VideoId>
<Title>Movie 1</Title>
</Movie>
<Movie>
<VideoId>2</VideoId>
<Title>Movie 2</Title>
</Movie>
</Items>
</ArrayOfMovie>
You could do it after serialization. The code skeleton looks like:
using (MemoryStream ms = new MemoryStream())
{
XmlWriterSettings settings = new XmlWriterSettings();
settings.Indent = true;
using (var writer = XmlWriter.Create(ms, settings))
{
var serializer = new XmlSerializer(typeof(List<Movie>));
serializer.Serialize(writer, tmpList);
}
ms.Position = 0;
XDocument doc = XDocument.Load(new XmlTextReader(ms));
doc.Root.Add(new XAttribute("customAttribute", "Yes"));
doc.Save(filename);
}
You would want to wrap the List<Movie> inside a class and then serialize that.
Something like the following
class Program
{
static void Main(string[] args)
{
string fileName = "abcd2.xml";
string serializationFile = Path.Combine(#"C:\", fileName);
List<Movie> tmpList = new List<Movie>();
tmpList.Add(new Movie() { VideoId = "1", Title = "Hello" });
tmpList.Add(new Movie() { VideoId = "2", Title = "ABCD" });
MovieList list = new MovieList("Yes", tmpList);
XmlWriterSettings settings = new XmlWriterSettings();
settings.Indent = true;
using (var writer = XmlWriter.Create(serializationFile, settings))
{
var serializer = new XmlSerializer(typeof(MovieList));
serializer.Serialize(writer, list);
}
}
}
public class MovieList
{
private string custom;
private List<Movie> movies;
public MovieList() { }
public MovieList(string custom, List<Movie> movies)
{
this.movies = movies;
this.custom = custom;
}
[XmlAttribute]
public string CustomAttribute
{
get { return this.custom; }
set { this.custom = value; }
}
public List<Movie> Movies
{
get
{
return movies;
}
set
{
this.movies = value;
}
}
}
public class Movie
{
public string VideoId { get; set; }
public string Title { get; set; }
}
The code can be improved a lot. This is just an example snippet. Check out the following MSDN link: http://msdn.microsoft.com/en-us/library/58a18dwa(v=vs.110).aspx

Categories