This question has been asked a lot of times on SO, but neither solution helped me.
My datastruct is serialized into an XML file using dataContractSerializer. The (de)serialization code is the following:
public static void serialize<T>(T xObject, string xFilePath, string xIndent = "")
{
XmlWriterSettings xSettings = ( xIndent == "" ? new XmlWriterSettings {Indent = false } : new XmlWriterSettings { Indent = true, IndentChars = xIndent } );
using (XmlWriter xStream = XmlWriter.Create(xFilePath, xSettings))
new DataContractSerializer(typeof(T)).WriteObject(xStream, xObject);
}
public static T deserialize<T>(string xFilePath)
{
using (FileStream xStream = new FileStream(xFilePath, FileMode.Open, FileAccess.Read, FileShare.ReadWrite))
{
DataContractSerializer xSerializer = new DataContractSerializer(typeof(T));
return (T)xSerializer.ReadObject(xStream);
}
}
A snippet of the written XML is
<?xml version="1.0" encoding="utf-8"?>
<PxePriceListEod xmlns:i="http://www.w3.org/2001/XMLSchema-instance" xmlns="libTypes.salesApp">
<DateOfValidity xmlns:d2p1="libTypes">
<d2p1:_x002E_>2016-09-09T00:00:00</d2p1:_x002E_>
</DateOfValidity>
<PriceRecords>
<AbstractPxePriceRecordEod i:type="PxePriceRecordEodBal">
<Product xmlns:d4p1="libTypes">
<d4p1:Ccy>
<d4p1:_x002E_>EUR</d4p1:_x002E_>
</d4p1:Ccy>
<d4p1:Commodity>
<d4p1:_x002E_>Electricity</d4p1:_x002E_>
</d4p1:Commodity>
<d4p1:Duration>
<d4p1:_x002E_>Month</d4p1:_x002E_>
</d4p1:Duration>
<d4p1:Exchange>
<d4p1:_x002E_>Pxe</d4p1:_x002E_>
</d4p1:Exchange>
<d4p1:Period>
<d4p1:_x002E_>9</d4p1:_x002E_>
</d4p1:Period>
<d4p1:Type>
<d4p1:_x002E_>Base</d4p1:_x002E_>
</d4p1:Type>
<d4p1:Year>
<d4p1:_x002E_>2016</d4p1:_x002E_>
</d4p1:Year>
</Product>
<IsDeduced xmlns:d4p1="libTypes">
<d4p1:_x002E_>false</d4p1:_x002E_>
</IsDeduced>
<IsInterpolated xmlns:d4p1="libTypes">
<d4p1:_x002E_>false</d4p1:_x002E_>
</IsInterpolated>
<IsSynthetic xmlns:d4p1="libTypes">
<d4p1:_x002E_>false</d4p1:_x002E_>
</IsSynthetic>
<Price xmlns:d4p1="libTypes">
<d4p1:_x002E_>30.45</d4p1:_x002E_>
</Price>
<DateOfValidity xmlns:d4p1="libTypes">
<d4p1:_x002E_>2016-09-09T00:00:00</d4p1:_x002E_>
</DateOfValidity>
</AbstractPxePriceRecordEod>
... more AbstractPxePriceRecordEod elements ...
</PriceRecords>
</PxePriceListEod>
Features of the problem:
The error points to Line=0, Position=1 (which does not make sense)
There is no element with name containing "."
All classes that make it into the file are properly decorated with DataContract
The XML file is checked to really be in UTF-8 encoding (when read by Notepad++) and none of the other versions of (de)serializing code listed on SO (that implicitly specify UTF-8 encoding) helped
I know the file is ugly (autogenerated element names like "d4p1:x002E" - Im the only dev in this company and unfortunately I dont have time to nicely decorate 100+ classes)
Everything was working fine for 2.5 years, the problems started today.
Any hint is much appreciated,
Daniel
UPDATE
I have added a minimal amount of code that reproduces the problem here. The application tries to read the given class from an xml file, the classes that have the problematic dataContractNames are located in library\+support\+qprimitive.
_x002E_ is how XmlConvert.EncodeLocalName() encodes the string ".". See https://dotnetfiddle.net/phUYO3 for a demonstration. So you either:
Have a data member with "." as its name.
Have implemented IXmlSerializable and are writing elements with this name.
That being said, making a data contract type with [DataMember(Name = ".")] on one of the data members does not cause problems for me. I.e. I can serialize and deserialize the following successfully:
[DataContract(Namespace = "libTypes.salesApp")]
public class PxePriceListEod
{
[DataMember]
public DateOfValidity DateOfValidity { get; set; }
}
// DateOfValidity
[DataContract(Namespace = "libTypes")]
public class DateOfValidity
{
[DataMember(Name = ".")]
public DateTime DateTime { get; set; }
}
Related
I want to serialize xml which works but I have a problem. Sometimes I have the same tag multiple times but on different places. So the order is important so I cant just stick it in an list.
For example I have the following xml:
<?xml version="1.0" encoding="utf-16"?>
<commands>
<execute>some statement</execute>
<wait>5</wait>
<execute>some statement</execute>
<wait>5</wait>
<execute>some statement</execute>
<execute>some statement</execute>
</commands>
Then my object would look something like this:
[XmlRoot(ElementName="commands")]
public class Commands {
[XmlElement(ElementName="execute")]
public List<string> Execute { get; set; }
[XmlElement(ElementName="wait")]
public List<int> Wait { get; set; }
}
If I then serialize it with the following function:
var xmlSerializer = new XmlSerializer(obj.GetType());
using (var writer = new Utf8StringWriter())
{
xmlSerializer.Serialize(writer, obj);
return writer.ToString();
}
The order will not be the same.... It would first serialize the execute tags and then the wait statements. While the order is important.
Does someone have a clue on how to tackle this problem?
Ps. changing the xml is not a solution as I'm tied to that....
Thanks in advance!
After searching for a while I tackle the problem as the following. The wait and execute are basically commands to I serialize now a list of commands. Of course the seriliazer will complain that it can't serlize this because it is a list of interfaces (ICommand) so I implemented the IXmlSerliazer so that I tell how to serliaze this.
This worked actually quite good
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 have a C# Web Service that is serializing my simple class:
[Serializable]
[XmlInclude(typeof(Bitmap))]
[XmlTypeAttribute(Namespace = "http://tempuri.org/")]
public class Class1
{
private static Bitmap _myImage = new Bitmap(#"C:\WebApplication1\ClassLibrary1\Untitled.png");
public Bitmap MyImage
{
get { return _myImage; }
set
{
_myImage = value;
}
}
}
Here's the asmx.cs code that does the serialization:
[WebMethod]
public string HelloWorld()
{
var c = new Class1();
XmlSerializer serializer = new XmlSerializer(typeof(Class1));
return XMLSerializer(c);
}
public string XMLSerializer(object pObject)
{
try
{
XmlSerializer xs = new XmlSerializer(pObject.GetType());
using (StringWriter stream = new StringWriter())
{
xs.Serialize(stream, pObject);
stream.Flush();
return stream.ToString();
}
}
catch (Exception ex)
{
return ex.ToString();
}
}
Looks prety straight forward. However, the XML generated by the XmlSerializer is producing and error when I try to DeSerialize it.
{"There is an error in XML document (5, 5)."}
{"Parameter is not valid."}
When I try to load the generated XML into IE I get this error.
Switch from current encoding to specified encoding not supported. Error processing resource 'file:///C:/Users/mhall15/Deskt...
<?xml version="1.0" encoding="utf-16"?>
Here's the generated XML:
<?xml version="1.0" encoding="utf-16"?>
<Class1 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<MyImage xmlns="http://tempuri.org/">
<Palette />
</MyImage>
</Class1>
Any ideas what's going on?
During the serialization, replace "encoding="utf-16" with "encoding="utf-8"" and that will cut it. The source of the problem - I'm not sure, but I've ran into it numerous times, and that's how I dealt with it.
That's how to deal with the IE issue.
The deserialization should be amongst these lines. I'm posting the kind of arbitrary code I normally use:
public static object DeserializeFromXML<T>(string _input)
{
object _temp = Activator.CreateInstance<T>();
Type expected_type = _temp.GetType();
_temp = null;
XmlSerializer serializer = new XmlSerializer(expected_type);
StringReader stringWriter = new StringReader(_input);
object _copy = serializer.Deserialize(stringWriter);
return _copy;
}
In the above example, I'm using templating for reusability sake. You should be able to call the method by saying DeserializeFromXML < Class1 >(_xml_input) where xml input is the string. That will force the compiler to use the definition of the given class to deserialize the XML input. That way you wouldn't even have to change the encoding in the serialization. If it's also a case where you may or may not know the data type to deserialize with, you can use a strategy design pattern where you register the root type of the XML with it's associated generic type. Later on you can reference that registry and arbitrarily deserialize any XML input as long as the root type is registered. It's a trick i normally use as well. If more is needed on this, let me know, and I'll explain in detail.
In addition,if you are running IE 9, the new update to IE 9 makes it difficult to view XML. Press F12 - go to developer tools and change your browser mode to run as IE 8 instead of IE 9.
I am using the .NET XmlSerializer class to deserialize GPX files.
There are two versions of the GPX standard:
<gpx xmlns="http://www.topografix.com/GPX/1/0"> ... </gpx>
<gpx xmlns="http://www.topografix.com/GPX/1/1"> ... </gpx>
Also, some GPX files do not specify a default namespace:
<gpx> ... </gpx>
My code needs to handle all three cases, but I can't work out how to get XmlSerializer to do it.
I am sure there must be a simple solution because this a common scenario, for example KML has the same issue.
I have done something similar to this a few times before, and this might be of use to you if you only have to deal with a small number of namespaces and you know them all beforehand. Create a simple inheritance hierarchy of classes, and add attributes to the different classes for the different namespaces. See the following code sample. If you run this program it gives the output:
Deserialized, type=XmlSerializerExample.GpxV1, data=1
Deserialized, type=XmlSerializerExample.GpxV2, data=2
Deserialized, type=XmlSerializerExample.Gpx, data=3
Here is the code:
using System;
using System.IO;
using System.Xml;
using System.Xml.Serialization;
[XmlRoot("gpx")]
public class Gpx {
[XmlElement("data")] public int Data;
}
[XmlRoot("gpx", Namespace = "http://www.topografix.com/GPX/1/0")]
public class GpxV1 : Gpx {}
[XmlRoot("gpx", Namespace = "http://www.topografix.com/GPX/1/1")]
public class GpxV2 : Gpx {}
internal class Program {
private static void Main() {
var xmlExamples = new[] {
"<gpx xmlns='http://www.topografix.com/GPX/1/0'><data>1</data></gpx>",
"<gpx xmlns='http://www.topografix.com/GPX/1/1'><data>2</data></gpx>",
"<gpx><data>3</data></gpx>",
};
var serializers = new[] {
new XmlSerializer(typeof (Gpx)),
new XmlSerializer(typeof (GpxV1)),
new XmlSerializer(typeof (GpxV2)),
};
foreach (var xml in xmlExamples) {
var textReader = new StringReader(xml);
var xmlReader = XmlReader.Create(textReader);
foreach (var serializer in serializers) {
if (serializer.CanDeserialize(xmlReader)) {
var gpx = (Gpx)serializer.Deserialize(xmlReader);
Console.WriteLine("Deserialized, type={0}, data={1}", gpx.GetType(), gpx.Data);
}
}
}
}
}
Here's the solution I came up with before the other suggestions came through:
var settings = new XmlReaderSettings();
settings.IgnoreComments = true;
settings.IgnoreProcessingInstructions = true;
settings.IgnoreWhitespace = true;
using (var reader = XmlReader.Create(filePath, settings))
{
if (reader.IsStartElement("gpx"))
{
string defaultNamespace = reader["xmlns"];
XmlSerializer serializer = new XmlSerializer(typeof(Gpx), defaultNamespace);
gpx = (Gpx)serializer.Deserialize(reader);
}
}
This example accepts any namespace, but you could easily make it filter for a specific list of known namespaces.
Oddly enough you can't solve this nicely. Have a look at the deserialize section in this troubleshooting article. Especially where it states:
Only a few error conditions lead to exceptions during the
deserialization process. The most common ones are:
•The name of the
root element or its namespace did not match the expected name.
...
The workaround I use for this is to set the first namespace, try/catch the deserialize operation and if it fails because of the namespace I try it with the next one. Only if all namespace options fail do I throw the error.
From a really strict point of view you can argue that this behavior is correct since the type you deserialize to should represent a specific schema/namespace and then it doesn't make sense that it should also be able to read data from another schema/namespace. In practice this is utterly annoying though. File extenstion rarely change when versions change so the only way to tell if a .gpx file is v0 or v1 is to read the xml contents but the xmldeserializer won't unless you tell upfront which version it will be.
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.