How to serialize XML array/list as sequentially named elements - c#

I need help serializing XML in C#/.net.
Given something like this in C#:
public class Action {
public string Name;
public string Type;
}
public class TestObject {
public List<Action> Actions;
}
I want to serialize the list of actions as elements, each with a unique name:
<TestObject>
<Action0>
<Name>A</Name>
<Type>Dog</Name>
</Action0>
<Action1>...</Action1>
<Action2>...</Action2>
...
</TestObject>
I've been looking into using IXmlSerializable on a custom List object to replace TestObject. But I'm not sure how to proceed.
Is this on the right track?
public class ActionCollection<T> : List<T>, IXmlSerializable ...
public class TestObject : ActionCollection<Action> { ...
The other possibility that comes to mind is - some way to customize the serialization of each Action to override the element name using C# code that could add the digit?

Using Xml Linq :
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Data;
using System.Xml;
using System.Xml.Linq;
namespace ConsoleApplication1
{
class Program
{
const string FILENAME = #"c:\temp\test.xml";
static void Main(string[] args)
{
TestObject tests = new TestObject() {
Actions = new List<Action>() {
new Action() { Name = "A", Type = "Dog"},
new Action() { Name = "C", Type = "Cat"},
new Action() { Name = "E", Type = "Elephant"}
}
};
string root = "<TestObject></TestObject>";
XDocument doc = XDocument.Parse(root);
XElement testObject = doc.Root;
int index = 0;
foreach (Action action in tests.Actions)
{
XElement newAction = new XElement("Action" + index.ToString(), new object[] {
new XElement("Name", action.Name),
new XElement("Type", action.Type)
});
testObject.Add(newAction);
index++;
}
doc.Save(FILENAME);
}
}
public class Action
{
public string Name;
public string Type;
}
public class TestObject
{
public List<Action> Actions;
}
}
Using a Custom Xml Serializer
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Data;
using System.Xml;
using System.Xml.Linq;
using System.Xml.Serialization;
using System.Xml.Schema;
namespace ConsoleApplication1
{
class Program
{
const string FILENAME = #"c:\temp\test.xml";
static void Main(string[] args)
{
TestObject tests = new TestObject() {
Actions = new List<Action>() {
new Action() { Name = "A", Type = "Dog"},
new Action() { Name = "C", Type = "Cat"},
new Action() { Name = "E", Type = "Elephant"}
}
};
XmlWriterSettings settings = new XmlWriterSettings();
settings.Indent = true;
XmlWriter writer = XmlWriter.Create(FILENAME,settings);
XmlSerializer serializer = new XmlSerializer(typeof(TestObject));
serializer.Serialize(writer, tests);
}
}
public class Action
{
public string Name;
public string Type;
}
public class TestObject : IXmlSerializable
{
public List<Action> Actions;
public void WriteXml(XmlWriter writer)
{
List<XElement> testObjects = new List<XElement>();
int index = 0;
foreach (Action action in Actions)
{
XElement newAction = new XElement("Action" + index.ToString(), new object[] {
new XElement("Name", action.Name),
new XElement("Type", action.Type)
});
testObjects.Add(newAction);
index++;
}
string obj = string.Join("", testObjects.Select(x => x.ToString()));
writer.WriteRaw("\n" + obj + "\n");
}
public void ReadXml(XmlReader reader)
{
}
public XmlSchema GetSchema()
{
return (null);
}
}
}

Related

Serializing Class having property of type object

The Property Value of Type object can be single value of any type like int, string, decimal etc. Or it can be a collection of values like List < int >, List < string > etc.
public class Criteria
{
[XmlElement("IDS")]
public object Value
{
get; set;
}
}
Current Output:
<CriteriaGroup>
<Criteria p2:type="Criteria" xmlns:p2="http://www.w3.org/2001/XMLSchema-instance">
<IDS p2:type="ArrayOfInt">
<int>2610</int>
<int>2452</int>
</IDS>
</Criteria>
<Criteria p2:type="Criteria" xmlns:p2="http://www.w3.org/2001/XMLSchema-instance">
<IDS p2:type="ArrayOfString">
<string>CUMULU1MO</string>
<string>ALLIEDWX2</string>
</IDS>
</Criteria>
</CriteriaGroup>
Expected Output:
How can I achieve below result? Tried decorating public object Value with XmlArray, XmlArrayItem but no luck.
<CriteriaGroup>
<Criteria>
<IDS>
<ID>2610</ID>
<ID>2452</ID>
</IDS>
</Criteria>
<Criteria>
<IDS>
<ID>CUMULU1MO</ID>
<ID>ALLIEDWX2</ID>
</IDS>
</Criteria>
</CriteriaGroup>
Used below method to Serialize.
public static string Serialize<T>(T data)
{
string xmlData = string.Empty;
XmlSerializer ser = new XmlSerializer(typeof(T));
XmlSerializerNamespaces ns = new XmlSerializerNamespaces();
ns.Add("", "");
var settings = new XmlWriterSettings();
settings.Indent = true;
settings.OmitXmlDeclaration = true;
using (StringWriter sw = new StringWriter())
{
using (XmlWriter xw = XmlWriter.Create(sw, settings))
{
ser.Serialize(xw, data, ns);
xmlData = sw.ToString();
}
}
return xmlData;
}
Try following :
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml;
using System.Xml.Serialization;
namespace ConsoleApplication1
{
class Program
{
const string FILENAME = #"c:\temp\test.xml";
static void Main(string[] args)
{
CriteriaGroup CriteriaGroup = new CriteriaGroup()
{
Criteria = new List<Criteria>() {
new Criteria() { Value = new string[] { "2610","2452"}
},
new Criteria() { Value = new string[] {"CUMULU1MO", "ALLIEDWX2"}
}
}
};
XmlWriterSettings settings = new XmlWriterSettings();
settings.Indent = true;
XmlWriter writer = XmlWriter.Create(FILENAME, settings);
XmlSerializer serializer = new XmlSerializer(typeof(CriteriaGroup));
serializer.Serialize(writer, CriteriaGroup);
}
}
public class CriteriaGroup
{
[XmlElement()]
public List<Criteria> Criteria { get; set; }
}
public class Criteria
{
[XmlArray("IDS")]
[XmlArrayItem("ID")]
public string[] Value
{
get;
set;
}
}
}

How I can deserialize a System.Xml.Linq.XElement?

I tried:
using (XmlReader reader = XmlReader.Create(_xmlPath))
{
while (reader.Read())
{
if (reader.NodeType.Equals(XmlNodeType.Element) && reader.Name.Equals("Obj"))
{
Obj obj = new Obj();
while (reader.Read())
{
if (reader.NodeType.Equals(XmlNodeType.EndElement) && reader.Name.Equals("Obj"))
break;
if (reader.NodeType.Equals(XmlNodeType.Element))
{
switch (reader.Name)
{
case "Code":
obj.Code = reader.ReadElementContentAsString();
break;
case "Data":
XElement el = (XElement) XNode.ReadFrom(reader);
XmlReader r = el.CreateReader();
XmlSerializer serializer = new XmlSerializer(typeof(Data));
Data data = (Data) serializer.Deserialize(r);
obj.Data = data;
break;
}
}
}
}
}
}
Xml :
<Root>
<Obj>
<Code>code</Code>
<Data>
<Date>2020-08-07</Date>
<Amount>1000</Amount>
</Data>
</Obj>
</Root>
Data class :
public class Data{
public DateTime Date {get;set;}
public decimal Amount {get;set;}
}
Normally, it's a large XML file, this is why I try to cut it to fragments and I use XmlReader to parse it.
The deserialization doesn't work, if you have other ways I'm a taker
Try following :
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);
Data data = doc.Descendants("Data")
.Select(x => new Data() { Date = (DateTime)x.Element("Date"), Amount = (decimal)x.Element("Amount") }).FirstOrDefault();
}
}
public class Data
{
public DateTime Date { get; set; }
public decimal Amount { get; set; }
}
}
For huge files use following :
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)
{
XmlReader reader = XmlReader.Create(FILENAME);
List<Data> data = new List<Data>();
while (!reader.EOF)
{
if (reader.Name != "Data")
{
reader.ReadToFollowing("Data");
}
if (!reader.EOF)
{
XElement xData = (XElement)XElement.ReadFrom(reader);
Data newData = new Data() { Date = (DateTime)xData.Element("Date"), Amount = (decimal)xData.Element("Amount") };
data.Add(newData);
}
}
}
}
public class Data
{
public DateTime Date { get; set; }
public decimal Amount { get; set; }
}
}
I simplified your code. It should work.
var serializer = new XmlSerializer(typeof(Obj));
using (XmlReader reader = XmlReader.Create(_xmlPath))
{
while (reader.ReadToFollowing("Obj"))
{
Obj obj = (Obj)serializer.Deserialize(reader);
}
}
If the actual xml has a more complex structure and this code doesn't work, let us know.

Parsing XML metadata containing conditional statements with C# in Unity

I have an XML file full of events like this one with multiple statements to check.
How is this action even called? I'm so lost that I don't even know where to start looking.
<BENEvents>
<BENDescisionQ1 flagBEN_00="true" flagBEN_01="true" flagBEN_28="true" flagBEN_29="false">
<AskQuestions caption='Looking today again at you glass with milk you decide....'>
<Question event='DescisionQ1Yes'>Cowcostumes look great.</Question>
<Question event='DescisionQ1No'>Flatnesss is justice, so NO</Question>
</AskQuestions>
</BENDescisionQ1>
<BENDescisionQ2 ....>
.....
</BENDescisionQ2>
<BENEvents>
Please help to point me in the right direction.
EDIT: THe answer solved my question, thatnk you
This is how my code ended up for now to deal with more general events.
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml;
using System.Xml.Linq;
using System.Text.RegularExpressions;
using UnityEngine;
using System.IO;
public class mllhildTestLinq : MonoBehaviour
{
public mllhildTestController controller;
public void QueryXML(string filename, string parentNode, GameObject listForDisplay)//(string[] args)
{
List<GeneralEvent> listEvents = new List<GeneralEvent>();
GeneralEvent GeneralEvents = new GeneralEvent();
listEvents = GeneralEvents.parse(filename, parentNode);
// just printing for testing
//Debug.Log("Is this shit getting here?");
int i = 0;
foreach (GeneralEvent newEvent in listEvents)
{
//Debug.Log(i);
listForDisplay.GetComponent<PrefabListButtoms>().AddNewButton(newEvent.nodeName, listForDisplay);
Debug.Log(newEvent.nodeName);
i++;
foreach (KeyValuePair<string, string> keyValuePair in newEvent.general)
{
Debug.Log(keyValuePair.Key + " " + keyValuePair.Value);
}
//for (int i = 0; i < newEvent.general.Count; i++)
//{
// controller.AddText(" key: " + newEvent.general[i].Key + " value: " + newEvent.general[i].Value);
//}
//controller.AddText("\n");
}
}
}
public class GeneralEvent
{
public static List<GeneralEvent> listEvents = new List<GeneralEvent>();
public string nodeName { get; set; }
public List<KeyValuePair<string, string>> general { get; set; }
//string patternNumber = #"\d+$";
string patternGeneral = #"[^\d]+[^\$]+";
public List<GeneralEvent> parse(string filename, string parentNode)
{
listEvents.Clear();
XElement generalEvents;
XDocument doc = XDocument.Load(filename);
try
{
generalEvents = doc.Descendants(parentNode).First();
//generalEvents = doc.Elements(parentNode).FirstOrDefault();
}
catch
{
generalEvents = null;
}
//XElement generalEvents = doc.Descendants(parentNode).FirstOrDefault();
if (generalEvents != null)
{
//Debug.Log("---------------------------------------------------------------------");
foreach (XElement descision in generalEvents.Elements())
{
GeneralEvent newEvent = new GeneralEvent();
listEvents.Add(newEvent);
//Debug.Log(descision.Name);
// newEvent.nodeName = string.Parse(Regex.Match(descision.Name.LocalName, patternGeneral).Value);
newEvent.nodeName = descision.Name.ToString();
newEvent.general = descision.Attributes()
.Select(x => new KeyValuePair<string, string>(Regex.Match(x.Name.LocalName, patternGeneral).Value, (string)x)).ToList();
}
}
else
{
Debug.Log("null");
}
return listEvents;
}
}
This is not a simple Xml Parse. See my Xml Linq solution using Regex
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml;
using System.Xml.Linq;
using System.Text.RegularExpressions;
namespace ConsoleApplication1
{
class Program
{
const string FILENAME = #"c:\temp\test.xml";
static void Main(string[] args)
{
BenEvent benEvent = new BenEvent();
benEvent.parse(FILENAME);
}
}
public class BenEvent
{
public static List<BenEvent> listEvents = new List<BenEvent>();
public int number { get; set; }
public List<KeyValuePair<int, Boolean>> flags { get; set; }
public string question { get; set; }
public List<KeyValuePair<string, string>> answers { get; set; }
string patternNumber = #"\d+$";
string patternYesNo = #"[^\d]+$";
public void parse(string filename)
{
XDocument doc = XDocument.Load(filename);
XElement benEvents = doc.Descendants("BENEvents").FirstOrDefault();
foreach (XElement descision in benEvents.Elements())
{
BenEvent newEvent = new BenEvent();
listEvents.Add(newEvent);
newEvent.number = int.Parse(Regex.Match(descision.Name.LocalName, patternNumber).Value);
newEvent.flags = descision.Attributes()
.Select(x => new KeyValuePair<int, Boolean>(int.Parse(Regex.Match(x.Name.LocalName, patternNumber).Value), (Boolean)x)).ToList();
newEvent.question = (string)descision.Element("AskQuestions").Attribute("caption");
newEvent.answers = descision.Descendants("Question")
.Select(x => new KeyValuePair<string, string>(Regex.Match((string)x.Attribute("event"), patternYesNo).Value, (string)x)).ToList();
}
}
}
}

C#, Ignore case sensitivity for XML deserialization

1.Xml file contains xml elements: "INSTITUTION" and "institution" and each xml element contains attributes like name,code and INN.
My code works fine if xml element name is:"INSTITUTION" and fails when it reaches to xml element name:"institution".
I found some solutions from stackoverflow but it didn't help me...
How can I ignore case sensitivity for xml deserialization?
<institutions>
<INSTITUTION name= "some_value" CODE="some_value" INN="some_value"/>
<INSTITUTION name= "some_value" CODE="some_value" INN="some_value"/>
<INSTITUTION name= "some_value" CODE="some_value" INN="some_value"/>
<institution name= "some_value" code="some_value" inn="some_value"/>
<institution name= "some_value" code="some_value" inn="some_value"/>
<institution name= "some_value" code="some_value" inn="some_value"/>
<institution name= "some_value" code="some_value" inn="some_value"/>
</institutions>
</treasury>
C# Code:
using System;
using System.Collections.Generic;
using System.IO;
using System.Text;
using System.Xml;
using System.Xml.Serialization;
using Test_for_finding_file_type.XmlSynonymDeserializer;
//This is the class that will be deserialized
[XmlRoot("treasury")]
public class Treasury
{
[XmlElement("institutions")]
public institutions Institutions { get; set; }
}
public class institutions
{
[XmlElement("INSTITUTION")]
public List<Institution> InstitutionList { get; set; }
}
public class Institution
{
[XmlAttribute("name")]
public string Name;
[XmlAttribute("CODE")]
public string Code;
[XmlAttribute("INN")]
public string Inn;
}
public class Program
{
public static void Main(String[] args)
{
Program pro = new Program();
pro.DeserializeObject("test.xml");
}
private void DeserializeObject(string filename)
{
Console.WriteLine("Reading with XML Reader");
XmlSerializer serializer = new XmlSerializer(typeof(Treasury));
FileStream fs = new FileStream(filename, FileMode.Open);
XmlReader reader = XmlReader.Create(fs);
Treasury treasuryAccounts;
treasuryAccounts = (Treasury)serializer.Deserialize(reader);
fs.Close();
Console.WriteLine("\n------------------------------------------Institutions---------------------------------------------------------\n");
foreach (var institition in treasuryAccounts.Institutions.InstitutionList)
{
Console.Write("Treasury Account Name:" + institition.Name
+ "\tCODE:" + institition.Code
+ "\tINN:" + institition.Inn
+ "\n\n"
);
}
Console.ReadKey();
}
}
The whole issue is the missing XmlRoot attribute. See code below :
using System;
using System.Collections.Generic;
using System.IO;
using System.Text;
using System.Xml;
using System.Xml.Serialization;
[XmlRoot("treasury")]
public class Treasury
{
[XmlElement("institutions")]
public Institutions institutions { get; set; }
}
[XmlRoot("institutions")]
public class Institutions
{
[XmlElement("INSTITUTION")]
public List<Institution> InstitutionList { get; set; }
}
public class Institution
{
[XmlAttribute("name")]
public string Name;
[XmlAttribute("CODE")]
public string Code;
[XmlAttribute("INN")]
public string Inn;
}
public class Program
{
const string FILENAME = #"c:\temp\test.xml";
public static void Main(String[] args)
{
Program pro = new Program();
pro.DeserializeObject(FILENAME);
}
private void DeserializeObject(string filename)
{
Console.WriteLine("Reading with XML Reader");
XmlSerializer serializer = new XmlSerializer(typeof(Institutions));
XmlReader reader = XmlReader.Create(FILENAME);
Treasury treasuryAccounts = new Treasury();
treasuryAccounts.institutions = (Institutions)serializer.Deserialize(reader);
reader.Close();
Console.WriteLine("\n------------------------------------------Institutions---------------------------------------------------------\n");
foreach (var institition in treasuryAccounts.institutions.InstitutionList)
{
Console.Write("Treasury Account Name:" + institition.Name
+ "\tCODE:" + institition.Code
+ "\tINN:" + institition.Inn
+ "\n\n"
);
}
Console.ReadKey();
}
}

Is it possible to make JavaScriptSerializer also populates properties without a setter?

Is it possible to make JavaScriptSerializer also populate properties without a setter? For example, a property like test.ID in the code below:
using System;
using System.Collections.Generic;
using System.Web.Script.Serialization;
namespace JavaScriptConverterTest
{
class Program
{
static void Main(string[] args)
{
List<test> list = new List<test>();
for (int i = 0; i < 2; i++)
{
list.Add(new test(Guid.NewGuid(), "Item #" + i));
}
JavaScriptSerializer serializer = new JavaScriptSerializer();
string jsonString = serializer.Serialize(list);
List<test> newList = serializer.Deserialize<List<test>>(jsonString);
Console.Read();
}
}
class test
{
private Guid id = Guid.Empty;
public Guid ID
{
get { return id; }
// Without a setter, JavaScriptSerializer doesn't populate this property.
// set { id = value; }
}
public string name = "";
public test()
{
}
public test(Guid id, string name)
{
this.id = id;
this.name = name;
}
}
}
You can use DataContractJsonSerializer which is built in .NET Framework and has its home at System.Runtime.Serialization.Json. You just need to decorate your field with DataMemberAttribute. Let's say you have this class:
class Foo
{
private string _boo;
public Foo(string boo) => _boo = boo;
public string Boo => _boo;
}
After decorating:
[DataContract]
class Foo
{
[DataMember] private string _boo;
public Foo(string boo) => _boo = boo;
public string Boo => _boo;
}
And testing:
private static void Main(string[] args)
{
var foo = new Foo("boo");
var serializer = new DataContractJsonSerializer(typeof(Foo));
string str;
using (var stream = new MemoryStream())
{
serializer.WriteObject(stream, foo);
str = Encoding.Default.GetString(stream.ToArray());
}
Console.WriteLine(str);
Foo loadedFoo;
using (var stream = new MemoryStream(Encoding.Default.GetBytes(str)))
{
loadedFoo = serializer.ReadObject(stream) as Foo;
}
Console.WriteLine(loadedFoo.Boo);
Console.ReadLine();
}
The loadedFoo that is constructed from the json string gets "boo" as value for _boo field.

Categories