This is an extension of a question I had beforehand
I have a specific function that I want to run, and it is located inside an XML File:
Console.WriteLine("Text for test, {0}, {1}", testWord, testWord2);
The text is stored in an XML file:
<?xml version="1.0" encoding="utf-8" ?>
<root>
<world>
<region name="TestRegion">
<area name="TestArea">
<building name="Outside">
<room name="TutorialRoom">
<textToDisplay>"Text for test, {0},{1}"</textToDisplay>
<extraString>testWord,tesWord2</extraString>
</room>
</building>
</area>
</region>
</world>
</root>
I can easily get the string data using LINQ
XElement xelement = XElement.Load("..\\..\\LocationDatabase.xml");
var textToDisplay= xelement.Elements("world")
.Elements("region").Where(region => (string)region.Attribute("name") == "TestRegion")
.Elements("area").Where(area => (string)area.Attribute("name") == "TestArea")
.Elements("building").Where(building => (string)building.Attribute("name") == "Outside")
.Elements("room").Where(room => (string)room.Attribute("name") == "TutorialRoom")
.Elements("textToDisplay");
var extraString= xelement.Elements("world")
.Elements("region").Where(region => (string)region.Attribute("name") == "TestRegion")
.Elements("area").Where(area => (string)area.Attribute("name") == "TestArea")
.Elements("building").Where(building => (string)building.Attribute("name") == "Outside")
.Elements("room").Where(room => (string)room.Attribute("name") == "TutorialRoom")
.Elements("extraString");
And this works completely fine. The issue I have is when I don't have a word in the XML file, but rather a property of a class. I have a singleton Player, and it has a autoproperty Name. To normally access it, I can just say:
Console.WriteLine("Your name is:", Player.Instance.Name);
But how do I, instead, keep this in the XML file? Like:
<?xml version="1.0" encoding="utf-8" ?>
<root>
<world>
<region name="TestRegion">
<area name="TestArea">
<building name="Outside">
<room name="TutorialRoom">
<textToDisplay>"Your name is: {0}"</textToDisplay>
<extraString>Player.Instance.Name</extraString>
</room>
</building>
</area>
</region>
</world>
</root>
When I use the past command, it simple thinks that whole section is a string, and outputs "Your name is: Player.Instance.Name"
An example using my own code:
The Player Class:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace GuardsOfAetheria
{
class Player
{
public enum Class
{
Melee,
Magic,
Ranged
}
public string Name { get; set; }
public Class PlayerClass { get; set; }
private static readonly Player instance = new Player();
static Player()
{
}
private Player()
{
}
public static Player Instance
{
get
{
return instance;
}
}
}
}
Is there a way to solve this?
EDIT 1:
I was able to do something similar using the following lines of code:
var typ = typeof(Player);
var prop = typ.GetProperty("Name");
var propVal = prop.GetValue(Player.Instance);
Console.WriteLine(((string)textToDisplay.First()).Replace(#"\n", Environment.NewLine), propVal);
This works fine, and gets the necessary data. The issue here is that in different parts, different classes have to be called var typ = typeof(Player), and different instances have to be attributed var propVal = prop.GetValue(Player.Instance). I can store the name of the class and instance I need to get from, but simply using a string that holds that data doesn't work, like below:
string className = "Player";
var typ = typeof(className);
var prop = typ.GetProperty("Name");
var propVal = prop.GetValue(Player.Instance);
Console.WriteLine(((string)textToDisplay.First()).Replace(#"\n", Environment.NewLine), propVal);
Is there anyway to do that?
Type player = Type.GetType("Player");
or
Type player = Type.GetType("myNamespace.Player");
See here for more info
Then you are going to have to get an instance of that type, see manipulating types. Then you will need to get the PropertyInfo's for the type, then get the one you want Instance, etc for Instance object.
You will need to get into intimate detail of telling the compiler what object your working with and what properties you want to access. Reflection is a runtime compiled way of accessing objects. It is definitely more complicated than the run-of-the-mill program.
Related
is there a way to force the XML Deserialize to convert only to an exact matching class object and throw an exception (or error) if it does not match exactly?
The following code will successfully import all 3 xml files:
using System.IO;
using System.Windows;
using System.Xml.Serialization;
namespace XmlDeserialize
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
public MainWindow()
{
Person data;
XmlSerializer xs = XmlSerializer.FromTypes(new[] { typeof(Person) })[0];
using (StreamReader reader = new StreamReader(Directory.GetCurrentDirectory() + "\\exact.xml"))
{
data = (Person)xs.Deserialize(reader);
}
using (StreamReader reader = new StreamReader(Directory.GetCurrentDirectory() + "\\reduced.xml"))
{
data = (Person)xs.Deserialize(reader);
}
using (StreamReader reader = new StreamReader(Directory.GetCurrentDirectory() + "\\extended.xml"))
{
data = (Person)xs.Deserialize(reader);
}
}
}
public class Person
{
public string Name;
public int Age;
}
}
exact.xml
<?xml version="1.0" encoding="utf-8"?>
<Person xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<Name>MyName</Name>
<Age>9999</Age>
</Person>
The exact.xml fits exactly and therefore it should be possible to deserialized it without any problem
reduced.xml
<?xml version="1.0" encoding="utf-8"?>
<Person xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<Name>MyName</Name>
</Person>
To be honest, I expected that I will receive an error by using reduced.xml, but it will be deserialized without a problem. I thought that if "Age" is not nullable (int?) I will receive an error and not the default value of an int. Spezially when keeping in mind that the following code will lead to a compiler error.
int x;
MessageBox.Show("x: " + x); //Error: Use of unassigned local variable 'x'
Therefore the successful deserialization is strange for me but somehow explainable....
But what I completely cannot understand is that fact that I do not receive an error when using the following xml file:
extended.xml
<?xml version="1.0" encoding="utf-8"?>
<Person xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<Name>MyName</Name>
<Age>9999</Age>
<Address>MyAddress</Address>
</Person>
The deserialization completely ignores that he is not able to assign "Address" to any class property....
Is it possible to force an exact parsing?
Best for me would be to receive an error by using reduced.xml and extended.xml but at least when using extended.xml I should receive an error....
BR,
MUT
First question on SO, apologies if I mess some of this up. I'm new to c# and LINQ and have spent the past 2 days searching SO for a solution, none seem to be exactly what I'm after. So...
The xml file I'm querying is generated from a DICOM Structured Report file. I'm trying to get specific values of elements from this xml file. These elements correspond to specific measurements that were taken during an ultrasound examination. The entire xml file is 15k lines long so for simplicity I've edited it. I'll just show one example of what I'm trying to do but the process will be the same for all other elements I'm looking to get.
The element I want to get has to meet 3 criteria, in this case Tricuspid Valve, Peak Velocity and Regurgitant Flow but this changes depending on the measurement that was taken. Once those criteria are met I want to get the value of , which in this case is 2120.
The xml
<report type="Comprehensive SR">
<document>
<content>
<container flag="SEPARATE">
<container flag="SEPARATE">
<code>
<meaning>Tricuspid Valve</meaning>
</code>
<container flag="SEPARATE">
<num>
<concept>
<meaning>Peak Velocity</meaning>
</concept>
<code>
<meaning>Regurgitant Flow</meaning>
</code>
<value>2120</value>
<unit>
<value>mm/s</value>
</unit>
</num>
My code in c#
XDocument xmlSR = XDocument.Load("DICOMSRtest.xml");
var TRVmax = from c in xmlSR.Descendants("container")
where (string)c.Element("code").Element("meaning") == "Tricuspid Valve"
where (string)c.Element("concept").Element("meaning") == "Peak Velocity"
where (string)c.Element("code").Element("meaning") == "Regurgitant Flow"
select c.Element("container").Element("num").Element("value");
Console.Write("TRVmax: " + TRVmax);
When I run the code I get the following
TRVmax: System.Linq.Enumerable+WhereSelectEnumerableIterator`2[System.Xml.Linq.XElement,System.Xml.Linq.XElement]
Any help or direction to some documentation which I can read to solve this would be greatly appreciated.
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
{
static void Main(string[] args)
{
XDocument doc = XDocument.Load("PurchaseOrder.xml");
List<XElement> xContainers = doc.Descendants("container").Where(x => x.Element("num") != null).ToList();
List<Container> containers = new List<Container>();
foreach (XElement xContainer in xContainers)
{
Container newContainer = new Container();
containers.Add(newContainer);
newContainer.code = (string)xContainer.Descendants("code").FirstOrDefault();
newContainer.concept = (string)xContainer.Descendants("concept").FirstOrDefault();
newContainer.value = (int)xContainer.Descendants("value").FirstOrDefault();
newContainer.unit = (string)xContainer.Descendants("unit").FirstOrDefault();
}
}
}
public class Container
{
public string code { get; set; }
public string concept { get; set; }
public int value { get; set; }
public string unit { get; set; }
}
}
I'm relatively new to Unity and C#. Actually, I mainly look at application code and try to learn a little bit. And that's fun.
Now I've stumbled upon a problem.
I'm trying to read an XML file and continue using the data from it. That even works. But now I don't want to use all records of the XML file, but only those that have a certain ID.
Currently I do it like this:
public class Data
{
public frage[] fragen = new frage[0];
public Data () { }
public static void Write(Data data)
{
XmlSerializer serializer = new XmlSerializer(typeof(Data));
using (Stream stream = new FileStream (GameUtility.XmlFilePath, FileMode.Create))
{
serializer.Serialize(stream, data);
}
}
public static Data Fetch ()
{
return Fetch(out bool result);
}
public static Data Fetch(out bool result)
{
if (!File.Exists(GameUtility.XmlFilePath)) { result = false; return new Data(); }
XmlSerializer deserializer = new XmlSerializer(typeof(Data));
using (Stream stream = new FileStream(GameUtility.XmlFilePath, FileMode.Open))
{
var data = (Data)deserializer.Deserialize(stream);
result = true;
return data;
}
}
}
This causes, I think, that all data is stored in the corresponding variable (data). But now I want only those data sets to be transferred that have the ID 5. Is this possible with simple adjustments or do I have to think about everything?
My data set, which is created via XML, looks like this:
<?xml version="1.0"?>
<Data xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<fragen>
<frage>
<info>IRGENDEINTEXT1</info>
<antworten>
<antwort>
<info>1</info>
<korrekt>true</korrekt>
</antwort>
<antwort>
<info>2</info>
<korrekt>false</korrekt>
</antwort>
<antwort>
<info>3</info>
<korrekt>false</korrekt>
</antwort>
<antwort>
<info>4</info>
<korrekt>false</korrekt>
</antwort>
</antworten>
<id>5</id>
</frage>
<frage>
<info>IRGENDEINTEXT2</info>
<antworten>
<antwort>
<info>1</info>
<korrekt>false</korrekt>
</antwort>
<antwort>
<info>2</info>
<korrekt>false</korrekt>
</antwort>
<antwort>
<info>3</info>
<korrekt>false</korrekt>
</antwort>
<antwort>
<info>4</info>
<korrekt>true</korrekt>
</antwort>
</antworten>
<id>7</id>
</frage>
</fragen>
</Data>
Thank you - I hope my question is meaningful and not too unclear. Forgive my incorrect English.
You could have, but I would advise against reading only partial files all the time because that is a constant IO hit. Every time you would need to fetch a new question you get another read IO on the disk. Unless your set of questions is humongous (e.g. several hundred MB), you could read all data into your Data object at game start and then just add a helper function into your Data class that supplies you with the relevant information. E.g.
class Data {
private Frage[] fragen;
// Read a Frage by ID
public Frage QuestionById(string id) {
return this.fragen.First(it => it.id == id);
}
}
Same for the answers:
class Frage {
private Antwort[] antworten;
public Antwort GetCorrectAnswer() {
return antworten.First(it => it.korrekt);
}
}
Then in your game logic you can just call:
var aFrage = data.QuestionById("4711");
var anAntwort = aFrage.GetCorrectAnswer();
You could theoretically also use XPath to just select the XML nodes you need, but for this to work you would still need to load the whole XML document into memory in order to run your XPath over it (I'm unaware of any XPath implementation for .net that would work on streams). So you may as well just use your own data structure as I have laid out.
If you really need a huge data set and cannot load everything into memory, you should maybe look at some database solution, e.g. SQLite to store your data. This would allow you to do SQL DB queries and doesn't require you to load all the data into memory.
I agree with Jan Thomä. What you do is called deserialization (when you create an xml or json from an object you do a serialization). So you just create an object from your xml file (of course tecnically you need to read the file to deserialize it but, of course, you use a library to handle it). Once you got the data you should be able to change your object or create a modified copy with your correct data.
Usually I prefer json so I'm not sure how to do it with XmlSerializer library (link with an example in the edit part) but you should create a bean object that is a representation of your xml file
(example antwort is one of the basic object and it should have a int info variable and a korrekt boolean variable, while antworten is an object that only contains a List of antwort and so on until you reach your main object fragen that contains all your basic rappresentation of your xml bean to deserialize (to create a bean just declare your variables and Getter/Setters for those variables))
After that you just deserialize your xml as a Fragen instead of as a Data type and you can than change your object the easy way.
EDIT. To undestand it better just look the example here:
https://learn.microsoft.com/it-it/dotnet/api/system.xml.serialization.xmlserializer.deserialize?view=netframework-4.8
you got the xml and the class that representate the object OrderedItem (note: the namespace should be optional but you need to call the variable with the same name of the variable in your xml and you should need getter and setter in OrderedItem class). Once you got OrderedItem item it's easy to read/write new value to the object, serialize a new xml with new data or just create a modified copy of your object.
You should just do the same thing.
Example from the link:
using System;
using System.IO;
using System.Xml.Serialization;
// This is the class that will be deserialized.
public class OrderedItem
{
[XmlElement(Namespace = "http://www.cpandl.com")]
public string ItemName;
[XmlElement(Namespace = "http://www.cpandl.com")]
public string Description;
[XmlElement(Namespace="http://www.cohowinery.com")]
public decimal UnitPrice;
[XmlElement(Namespace = "http://www.cpandl.com")]
public int Quantity;
[XmlElement(Namespace="http://www.cohowinery.com")]
public decimal LineTotal;
// A custom method used to calculate price per item.
public void Calculate()
{
LineTotal = UnitPrice * Quantity;
}
}
public class Test
{
public static void Main()
{
Test t = new Test();
// Read a purchase order.
t.DeserializeObject("simple.xml");
}
private void DeserializeObject(string filename)
{
Console.WriteLine("Reading with Stream");
// Create an instance of the XmlSerializer.
XmlSerializer serializer =
new XmlSerializer(typeof(OrderedItem));
// Declare an object variable of the type to be deserialized.
OrderedItem i;
using (Stream reader = new FileStream(filename, FileMode.Open))
{
// Call the Deserialize method to restore the object's state.
i = (OrderedItem)serializer.Deserialize(reader);
}
// Write out the properties of the object.
Console.Write(
i.ItemName + "\t" +
i.Description + "\t" +
i.UnitPrice + "\t" +
i.Quantity + "\t" +
i.LineTotal);
}
}
xml
<?xml version="1.0"?>
<OrderedItem xmlns:inventory="http://www.cpandl.com" xmlns:money="http://www.cohowinery.com">
<inventory:ItemName>Widget</inventory:ItemName>
<inventory:Description>Regular Widget</inventory:Description>
<money:UnitPrice>2.3</money:UnitPrice>
<inventory:Quantity>10</inventory:Quantity>
<money:LineTotal>23</money:LineTotal>
</OrderedItem>
i wrote an application which is a custom console that allows execution of various commands. One of the commands allows serialization of data. The input data is a string, which is a list of comma separated values.
My question is - how to make the serialized data compact as much as possible?
The serialization format is not important for me.
Here is the command's code:
using CustomConsole.Common;
using System.IO;
using System.Xml.Serialization;
using System;
namespace Shell_Commander.Commands
{
public class SerializeCommand : ICommand
{
private string _serializeCommandName = "serialize";
public string Name { get { return this._serializeCommandName; } set { _serializeCommandName = value; } }
public string Execute(string parameters)
{
try
{
var splittedParameters = parameters.Split(" ");
var dataToSerialize = splittedParameters[0].Split(",");
var pathTofile = splittedParameters[1].Replace(#"\", #"\\");
XmlSerializer serializer = new XmlSerializer(dataToSerialize.GetType());
using (StreamWriter writer = new StreamWriter(pathTofile))
{
serializer.Serialize(writer, dataToSerialize);
var length = new FileInfo(pathTofile).Length;
Console.WriteLine($"Wrote file to: {pathTofile}");
return length.ToString();
}
}
catch (Exception e)
{
Console.WriteLine(e);
return "0";
}
}
}
}
The command accepts 2 parameters:
Data to serialize
File path (in order to save the serialized data).
Example - for the "1,2,4" input, the following file will be saved:
<?xml version="1.0" encoding="utf-8"?>
<ArrayOfString xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<string>1</string>
<string>2</string>
<string>4</string>
</ArrayOfString>
EDIT:
I want my command to be able to serialize also complex objects in the future, so writing the string as is to the file is not a solution.
I want to use only standard serialization methods and formats.
Okay this one DID it! Thanks to all of you!
public class Result
{
public String htmlEscaped
{
set;
get;
}
[XmlIgnore]
public String htmlValue
{ set; get; }
[XmlElement("htmlValue")]
public XmlCDataSection htmlValueCData
{
get
{
XmlDocument _dummyDoc = new XmlDocument();
return _dummyDoc.CreateCDataSection(htmlValue);
}
set { htmlValue = (value != null) ? value.Data : null; }
}
}
Result r = new Result();
r.htmlValue = ("<b>Hello</b>");
r.htmlEscaped = ("<b>Hello</b>");
XmlSerializer xml = new XmlSerializer(r.GetType());
TextWriter file = new StreamWriter(Environment.CurrentDirectory + "\\results\\result.xml", false, System.Text.Encoding.Default);
xml.Serialize(file, r);
file.Close();
RESULT:
<?xml version="1.0" encoding="Windows-1252"?>
<Result xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<htmlEscaped><b>Hello</b></htmlEscaped>
<htmlValue><![CDATA[<b>Hello</b>]]></htmlValue>
</Result>
As you can see, after CDATA is return type, no more escaped html in XML file on filesystem.
The JSON Serialization isn't working anymore, but this can be fixed with a little type extention.
QUESTION WAS:
Maybe someone knows how to make do it...
I have this Class:
public class Result
{
public String htmlValue
{
get;
set;
}
}
I use this to serialize it to XML
Result res = new Result();
res.htmlValue = "<p>Hello World</p>";
XmlSerializer s = new XmlSerializer(res.GetType());
TextWriter w = new StreamWriter(Environment.CurrentDirectory + "\\result.xml", false, System.Text.Encoding.Default);
s.Serialize(w, res);
w.Close();
Works fine i get this:
<?xml version="1.0" encoding="Windows-1252"?>
<Result xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<htmlValue><b>Hello World</b></htmlValue>
</Result>
What can do i have to change to get this:
<?xml version="1.0" encoding="Windows-1252"?>
<Result xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<htmlValue><![CDATA[<b>Hello World</b>]]></htmlValue>
</Result>
I've already searched but I can't find anything. The type of htmlValue
have to stay String, because of other Serialisations JSON, etc.
Tricky one... Thanks in advance for suggestions
HTML is correct in String within C#. Why decode or encode?
XmlSerializer saved the HTML escaped to XML file.
Don't use C# for consuming.
Is external tool which accept this:
<htmlValue><![CDATA[<b>Hello World</b>]]></htmlValue>
but not
<htmlValue><b>Hello World</b></htmlValue>
I do the same with JSON Serializer, in file on hard drive the HTML is saved correct.
Why and where to use HTTP Utility to prevent that? And how to get <![CDATA[ ]]> around it.
Can you give a code sample?
Are there any other Serializer than the C# own one?
I've found this Link .NET XML Serialization of CDATA ATTRIBUTE from Marco André Silva, which does I need to do, but it's different, how to include this without changing Types?
Here's a simple trick to do achieve what you want. You just need to serialize a XmlCDataSection property instead of the string property :
(it's almost the same as John's suggestion, but a bit simpler...)
public class Result
{
[XmlIgnore]
public String htmlValue
{
get;
set;
}
private static XmlDocument _dummyDoc;
[XmlElement("htmlValue")]
public XmlCDataSection htmlValueCData
{
get { return _dummyDoc.CreateCDataSection(htmlValue); }
set { htmlValue = (value != null) ? value.Data : null; }
}
}
See "CDATA serialization with XMLSerializer" for the same problem, and for the solution.
BTW, it seems to me that if the vendor no longer exists, it's time to use a different product. Possibly one that understands the XML specifications which have only existed for over a decade.
It is my understanding that you need the XML to feed it to some utility. Do you also plan to use it to de-serialize the object?
If not then why do not do it yourself - serialize your object that is? Roundtrip object -> XML -> object is somewhat tricky, but the first part is not.