I am using WinForms. I have an XML document that looks like this:
<?xml version="1.0" encoding="utf-8" ?>
<MarcusXMLFile xmlns:Responses="http://www.rewardstrike.com/XMLFile1.xml">
<response>
<greatmood>
<yes>
<replytocommand>
<answer>Yes.</answer>
<answer>Yes, sir.</answer>
<answer>Settings.Default.User</answer>
</replytocommand>
</yes>
</greatmood>
</response>
</MarcusXMLFile>
To read this xml document, I use:
private void Responses()
{
string query = String.Format("http://www.rewardstrike.com/XMLFile1.xml");
XmlDocument Responses = new XmlDocument();
Responses.Load(query);
XmlNode channel = Responses.SelectSingleNode("MarcusXMLFile");
if (QEvent == "yesreplytocommand")
{
XmlNodeList yesreplytocommand = Responses.SelectNodes("MarcusXMLFile/response/greatmood/yes/replytocommand/answer");
foreach (XmlNode ans in yesreplytocommand
.Cast<XmlNode>()
.OrderBy(elem => Guid.NewGuid()))
{
response = ans.InnerText;
}
}
}
and then to display:
QEvent = "yesreplytocommand";
Responses();
Console.WriteLine(response);
My problem is when it gets Settings.Default.User and displays it, I want it to display it as c# code so that it actually gets the value from the application. Right now it is actually displaying "Settings.Default.User". How do I do this?
First, you'll need a way to recognize which of your entries are literals and which are expressions. You could do it by adding an attribute to the XML node:
<?xml version="1.0" encoding="utf-8" ?>
<MarcusXMLFile xmlns:Responses="http://www.rewardstrike.com/XMLFile1.xml">
<response>
<greatmood>
<yes>
<replytocommand>
<answer>Yes.</answer>
<answer>Yes, sir.</answer>
<answer expression="true">DefaultSettings.User</answer>
</replytocommand>
</yes>
</greatmood>
</response>
</MarcusXMLFile>
Based on that you can modify your parsing code to either directly use the value from XML or evaluate it instead:
foreach (XmlNode ans in yesreplytocommand
.Cast<XmlNode>()
.OrderBy(elem => Guid.NewGuid()))
{
var attribute = ans.Attributes["expression"];
if (attribute != null && attribute.Value == "true")
{
Console.WriteLine(Evaluate(ans.InnerText));
}
else
{
Console.WriteLine(ans.InnerText);
}
}
There's still the problem of evaluating that expression. There's no easy built-in way to do that from C#. But you could use Dynamic Expresso. This is how Evaluate method could look like:
public string Evaluate(string expression)
{
var interpreter = new Interpreter();
interpreter.SetVariable("DefaultSettings", Settings.Default);
return interpreter.Eval<string>(expression);
}
As you can see, you'll still have to define the expression variables yourself. For the above to work, you will have to use DefaultSettings.User in your XML instead of Settings.Default.User. I already made that change in my sample XML at the beginning of the answer.
You should take a look at XML Serialization.
Really basic on how it works is that it can convert a struct or a class like this:
struct Foo
{
int bar = 0;
Vector2 obj = new Vector2(10, 50);
}
into this:
<?xml version="1.0" ?>
<Foo xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema"">
<bar>0</bar>
<obj>
<X>10</X>
<Y>50</Y>
</ojb>
</Foo>
And the other way around.
The methods used to load and save code looks like this:
public static void Save(string filepath, Foo foobject)
{
XmlSerializer serializer = new XmlSerializer(typeof(Foo));
using (Stream stream = File.OpenWrite(filepath))
{
serializer.Serialize(stream, foobject);
}
}
public static Foo Load(string filepath)
{
Foo myFoo;
XmlSerializer serializer = new XmlSerializer(typeof(Foo));
using (Stream stream = File.OpenRead(filepath))
{
myFoo = (Foo)serializer.Deserialize(stream);
}
}
It converts xml code to c# code, and other way around.
It cannot convert methods, but it can convert most properties and classes.
Related
I am trying to serialize and deserialize responses I am getting from service. The response is xml. The problem is that response can contain namespaces.
This is a response example which contains xmlns in root element and xmlns:ns2 in child:
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<RootElement version="4.0" xmlns="http://www.test.com/test">
<Verification xmlns="" xmlns:ns2="http://www.test.com/test">
<!--some fields-->
</Verification>
</RootElement>
This is another response which does not contain xmlns and xmlns:n2:
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<RootElement version="2.0">
<Verification>
<!--some fields-->
</Verification>
</RootElement>
To deserialize this response and then to serialize again I am using this code:
public static object XmlDeserializeFromString(this string objectData, Type type)
{
var serializer = new XmlSerializer(type);
object result;
using (TextReader textReader = new StringReader(objectData))
{
using (XmlTextReader xmlReader = new XmlTextReader(textReader))
{
// xmlReader.Namespaces = false;
result = serializer.Deserialize(xmlReader);
}
}
return result;
}
public static string XmlSerializeToString(this object objectInstance, System.Text.Encoding encoding)
{
var serializer = new XmlSerializer(objectInstance.GetType());
var sb = new StringBuilder();
var settings = new XmlWriterSettings
{
Encoding = encoding ?? System.Text.Encoding.UTF8,
Indent = true,
NewLineHandling = NewLineHandling.Replace
};
using (var sw = new EncodedStringWriter(encoding, sb))
{
using (var xw = XmlWriter.Create(sw, settings))
{
serializer.Serialize(xw, objectInstance);
}
}
return sb.ToString();
}
The class: This works for first type or responses but not for second. If I'll remove Namespace from attribute it will work for second but not for first type.
[Serializable, XmlRoot("RootElement", IsNullable = false, Namespace="http://www.test.com/test")]
public class RootElement
{
[XmlAttribute("version")] public string Version;
[XmlElement(Namespace="http://www.test.com/test")]
public Verification[] Verification;
}
public class Verification
{
[XmlAttribute("vendor")] public string Vendor;
}
with xmlReader.Namespaces = false line uncommented deserialization fails for first example, with commented it fails for second example. I tried to override the NamespaceURL property from XmlTextReader to ignore namespaces like it is described here and in other questions and answers and articles, also tried to override Read() method but it does not helped. I also tried to add or remove urls from class attributes but it does not helped too.
Is it possible to create a serialize and deserialize methods which can do the job for both responses? What I am doing wrong?
Im trying to deserialize an array of objects from a XML Document.
The document built in the following structure:
<?xml version="1.0" encoding="UTF-8"?>
<root>
<element>
.....
</element>
<element>
.....
</element>
</root>
But for some reason Im having lots of problems doing so.
This is my function which I call to deserialize it:
public static CardModel[] Load(string text)
{
XmlRootAttribute xRoot = new XmlRootAttribute();
xRoot.ElementName = "root";
xRoot.IsNullable = true;
XmlSerializer serializer = new XmlSerializer(typeof(CardModel[]),xRoot);
StringReader reader = new StringReader(text);
CardModel[] o = serializer.Deserialize(reader) as CardModel[];
reader.Close();
return o;
}
And I am not sure if its done correctly or not. Because I know that in json you are unable to deserialize an array and you have to do some sort of "hack".
In the CardModel class (which is the element of the array) i use above the class the tag [XmlRoot("root")]. I have also tried to use [XmlRoot("element")] but still im getting stuck.
Afaik you can't directly deserialize into an array but would need a wrapper class like
[Serializable]
[XMLRoot("root")]
public class Root
{
// This does the magic of treating all "element" items nested under the root
// As part of this array
[XmlArray("element")]
public CardModel[] models;
}
And rather deserilialize into that like
public static CardModel[] Load(string text)
{
// I don't think that you need the attribute overwrite here
var serializer = new XmlSerializer(typeof(Root));
using(var reader = new StringReader(text))
{
var root = (Root) serializer.Deserialize(reader);
return root.models;
}
}
I have this class:
public class MyMenu
{
public string Name { get; set; }
public string Type { get; set; }
}
This class I want to use it in a dynamic menu, and I do not want to store his data in a database.
I want to store its data in Xml file.
Till now I have this for saving data:
string path = Server.MapPath("~/Content/Files");
XmlSerializer serial = new XmlSerializer(model.GetType());
System.IO.StreamWriter writer = new System.IO.StreamWriter(path + "\\ribbonmenu");
serial.Serialize(writer, model);
writer.Close();
And this to get the data:
string path = Server.MapPath("~/Content/Files");
XmlSerializer serial = new XmlSerializer(typeof(RibbonMenu));
System.IO.StreamReader reader = new System.IO.StreamReader(path + "\\ribbonmenu");
RibbonMenu menu =(RibbonMenu) serial.Deserialize(reader);
reader.Close();
What I have is working for one object to save and get.
I need to save multiple objects, and get the collection of objects, something like:
IEnumerable<MyMenu> model=(IEnumerable<MyMenu>) serial.Deserialize(reader);
Can someone give me a solution? Thanks.
Edit: The content of the generated Xml with my code is:
<?xml version="1.0" encoding="utf-8"?>
<MyMenu xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<Id>0</Id>
<Menu>Home</Menu>
<Type>Button</Type>
</MyMenu>
When serializing you should initialize a collection like this:
var model = new List<MyMenu>()
{
new MyMenu() { Name = "Menu1", Type = "Ribbon" },
new MyMenu() { Name = "Menu2", Type = "Ribbon" },
};
This way, when you serialize you'd get something like this:
<?xml version="1.0" encoding="utf-8"?>
<ArrayOfMyMenu xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<MyMenu>
<Name>Menu1</Name>
<Type>Ribbon</Type>
</MyMenu>
<MyMenu>
<Name>Menu2</Name>
<Type>Ribbon</Type>
</MyMenu>
</ArrayOfMyMenu>
And you can get the object back by using List as the type of serializer:
XmlSerializer serial = new XmlSerializer(typeof(List<MyMenu>));
System.IO.StreamReader reader = new System.IO.StreamReader("ribbonmenu.xml");
var menu = (List<MyMenu>)serial.Deserialize(reader);
reader.Close();
Hope this helps.
I have an Xml file as:
<?xml version="1.0"?>
<hashnotes>
<hashtags>
<hashtag>#birthday</hashtag>
<hashtag>#meeting</hashtag>
<hashtag>#anniversary</hashtag>
</hashtags>
<lastid>0</lastid>
<Settings>
<Font>Arial</Font>
<HashtagColor>red</HashtagColor>
<passwordset>0</passwordset>
<password></password>
</Settings>
</hashnotes>
I then call a function to add a node in the xml,
The function is :
public static void CreateNoteNodeInXDocument(XDocument argXmlDoc, string argNoteText)
{
string lastId=((Convert.ToInt32(argXmlDoc.Root.Element("lastid").Value)) +1).ToString();
string date = DateTime.Now.ToString("MM/dd/yyyy");
argXmlDoc.Element("hashnotes").Add(new XElement("Note", new XAttribute("ID", lastId), new XAttribute("Date",date),new XElement("Text", argNoteText)));
//argXmlDoc.Root.Note.Add new XElement("Text", argNoteText)
List<string> hashtagList = Utilities.GetHashtagsFromText(argNoteText);
XElement reqNoteElement = (from xml2 in argXmlDoc.Descendants("Note")
where xml2.Attribute("ID").Value == lastId
select xml2).FirstOrDefault();
if (reqNoteElement != null)
{
foreach (string hashTag in hashtagList)
{
reqNoteElement.Add(new XElement("hashtag", hashTag));
}
}
argXmlDoc.Root.Element("lastid").Value = lastId;
}
After this I save the xml.
Next time when I try to load the Xml, it fails with an exception:
System.Xml.XmlException: Unexpected XML declaration. The XML declaration must be the first node in the document, and no white space characters are allowed to appear before it.
Here is the code to load the XML:
private static XDocument hashNotesXDocument;
private static Stream hashNotesStream;
StorageFile hashNoteXml = await InstallationFolder.GetFileAsync("hashnotes.xml");
hashNotesStream = await hashNoteXml.OpenStreamForWriteAsync();
hashNotesXDocument = XDocument.Load(hashNotesStream);
and I save it using:
hashNotesXDocument.Save(hashNotesStream);
You don't show all of your code, but it looks like you open the XML file, read the XML from it into an XDocument, edit the XDocument in memory, then write back to the opened stream. Since the stream is still open it will be positioned at the end of the file and thus the new XML will be appended to the file.
Suggest eliminating hashNotesXDocument and hashNotesStream as static variables, and instead open and read the file, modify the XDocument, then open and write the file using the pattern shown here.
I'm working only on desktop code (using an older version of .Net) so I can't test this, but something like the following should work:
static async Task LoadUpdateAndSaveXml(Action<XDocument> editor)
{
XDocument doc;
var xmlFile = await InstallationFolder.GetFileAsync("hashnotes.xml");
using (var reader = new StreamReader(await xmlFile.OpenStreamForReadAsync()))
{
doc = XDocument.Load(reader);
}
if (doc != null)
{
editor(doc);
using (var writer = new StreamWriter(await xmlFile.OpenStreamForWriteAsync()))
{
// Truncate - https://stackoverflow.com/questions/13454584/writing-a-shorter-stream-to-a-storagefile
if (writer.CanSeek && writer.Length > 0)
writer.SetLength(0);
doc.Save(writer);
}
}
}
Also, be sure to create the file before using it.
I have this code:
public class Hero
{
XmlReader Reader = new XmlTextReader("InformationRepositories/HeroRepository/HeroInformation.xml");
XmlReaderSettings XMLSettings = new XmlReaderSettings();
public ImageSource GetHeroIcon(string Name)
{
XMLSettings.IgnoreWhitespace = true;
XMLSettings.IgnoreComments = true;
Reader.MoveToAttribute(" //I'm pretty much stuck here.
}
}
And this is the XML file I want to read from:
<?xml version="1.0" encoding="utf-8" ?>
<Hero>
<Legion>
<Andromeda>
<HeroType>Agility</HeroType>
<Damage>39-53</Damage>
<Armor>3.1</Armor>
<MoveSpeed>295</MoveSpeed>
<AttackType>Ranged(400)</AttackType>
<AttackRate>.75</AttackRate>
<Strength>16</Strength>
<Agility>27</Agility>
<Intelligence>15</Intelligence>
<Icon>Images/Hero/Andromeda.gif</Icon>
</Andromeda>
</Legion>
<Hellbourne>
</Hellbourne>
</Hero>
I'm tring to get the ,/Icon> element.
MoveToAttribute() won't help you, because everything in your XML is elements. The Icon element is a subelement of the Andromeda element.
One of the easiest ways of navigating an XML document if you're using the pre-3.5 xml handling is by using an XPathNavigator. See this example for getting started, but basically you just need to create it and call MoveToChild() or MoveToFollowing() and it'll get you to where you want to be in the document.
XmlDocument doc = new XmlDocument();
doc.Load("InformationRepositories/HeroRepository/HeroInformation.xml");
XPathNavigator nav = doc.CreateNavigator();
if (nav.MoveToFollowing("Icon",""))
Response.Write(nav.ValueAsInt);
Note that an XPathNavigator is a forward only mechanism, so it can be problematic if you need to do looping or seeking through the document.
If you're just reading XML to put the values into objects, you should seriously consider doing this automatically via object serialization to XML. This would give you a painless and automatic way to load your xml files back into objects.
Mark your attributes in your object according to the element you want to load them to:
See: http://msdn.microsoft.com/en-us/library/system.xml.serialization.xmlattributeattribute.aspx
If, for some reason, you can't do this to your current object, consider making a bridge object which mirrors your original object and add a AsOriginal() method which returns the Original Object.
Working off the msdn example:
public class GroupBridge
{
[XmlAttribute (Namespace = "http://www.cpandl.com")]
public string GroupName;
[XmlAttribute(DataType = "base64Binary")]
public Byte [] GroupNumber;
[XmlAttribute(DataType = "date", AttributeName = "CreationDate")]
public DateTime Today;
public Group AsOriginal()
{
Group g = new Group();
g.GroupName = this.GroupName;
g.GroupNumber = this.GroupNumber;
g.Today = this.Today;
return g;
}
}
public class Group
{
public string GroupName;
public Byte [] GroupNumber;
public DateTime Today;
}
To Serialize and DeSerialize from LINQ objects, you can use:
public static string SerializeLINQtoXML<T>(T linqObject)
{
// see http://msdn.microsoft.com/en-us/library/bb546184.aspx
DataContractSerializer dcs = new DataContractSerializer(linqObject.GetType());
StringBuilder sb = new StringBuilder();
XmlWriter writer = XmlWriter.Create(sb);
dcs.WriteObject(writer, linqObject);
writer.Close();
return sb.ToString();
}
public static T DeserializeLINQfromXML<T>(string input)
{
DataContractSerializer dcs = new DataContractSerializer(typeof(T));
TextReader treader = new StringReader(input);
XmlReader reader = XmlReader.Create(treader);
T linqObject = (T)dcs.ReadObject(reader, true);
reader.Close();
return linqObject;
}
I don't have any example code of Serialization from non-LINQ objects, but the MSDN link should point you in the right direction.
You can use linq to xml:
public class XmlTest
{
private XDocument _doc;
public XmlTest(string xml)
{
_doc = XDocument.Load(new StringReader(xml);
}
public string Icon { get { return GetValue("Icon"); } }
private string GetValue(string elementName)
{
return _doc.Descendants(elementName).FirstOrDefault().Value;
}
}
you can use this regular exspression "<Icon>.*</Icon>" to find all the icons
then just remove the remove the tag, and use it....
would be a lot shorter
Regex rgx = new Regex("<Icon>.*</Icon>");
MatchCollection matches = rgx.Matches(xml);
foreach (Match match in matches)
{
string s= match.Value;
s= s.Remove(0,6)
s= s.Remove(s.LastIndexOf("</Icon>"),7);
console.Writeline(s);
}