extra closing bracket in xml - c#

I am using this example to save some variables to an xml file:
how do I set the current class to the return types results
This is my code for the settings file:
using System;
using System.IO;
using System.Xml.Serialization;
namespace ssscc.Settings
{
public class AppSettings
{
public string ReceiptLine1 { set; get; }
public string ReceiptLine2 { set; get; }
public string ReceiptLine3 { set; get; }
public string ReceiptLine4 { set; get; }
public string ReceiptLine5 { set; get; }
public string ReceiptLine6 { set; get; }
public bool ReceiptLine1Enabled { set; get; }
public bool ReceiptLine2Enabled { set; get; }
public bool ReceiptLine3Enabled { set; get; }
public bool ReceiptLine4Enabled { set; get; }
public bool ReceiptLine5Enabled { set; get; }
public bool ReceiptLine6Enabled { set; get; }
public string GatewayUserName { set; get; }
public string GatewayPassword { set; get; }
public string GatewayId { set; get; }
private static string GetSettingsFile()
{
var exePath = System.Windows.Forms.Application.StartupPath;
var sharedDirectory = Path.Combine(exePath, "shared");
var settingsDirectory = Path.Combine(sharedDirectory, "settings");
var settingsFile = Path.Combine(settingsDirectory, "ssscc.xml");
if (!Directory.Exists(sharedDirectory))
{
Directory.CreateDirectory(sharedDirectory);
}
if (!Directory.Exists(settingsDirectory))
{
Directory.CreateDirectory(settingsDirectory);
}
return settingsFile;
}
internal void SaveSettings()
{
var serializer = new XmlSerializer(typeof(AppSettings));
using (var stream = File.OpenWrite(GetSettingsFile()))
serializer.Serialize((Stream)stream, this);
}
internal static AppSettings GetInstance()
{
try
{
if (!File.Exists(GetSettingsFile()))
return null;
var serializer = new XmlSerializer(typeof(AppSettings));
using (var stream = File.OpenRead(GetSettingsFile()))
{
return (AppSettings)serializer.Deserialize(stream);
}
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
throw;
}
}
}
}
When I save the data the, the initial save goes fine and at the end of the file it shows:
<?xml version="1.0"?>
<AppSettings xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<ReceiptLine1 />
<ReceiptLine2 />
<ReceiptLine3 />
<ReceiptLine4 />
<ReceiptLine5 />
<ReceiptLine6 />
<ReceiptLine1Enabled>false</ReceiptLine1Enabled>
<ReceiptLine2Enabled>true</ReceiptLine2Enabled>
<ReceiptLine3Enabled>false</ReceiptLine3Enabled>
<ReceiptLine4Enabled>false</ReceiptLine4Enabled>
<ReceiptLine5Enabled>false</ReceiptLine5Enabled>
<ReceiptLine6Enabled>false</ReceiptLine6Enabled>
<GatewayUserName>asdfasdf</GatewayUserName>
<GatewayPassword>asdf</GatewayPassword>
<GatewayId>sdf</GatewayId>
</AppSettings>
When I update the file and save it again, I end up with this:
<?xml version="1.0"?>
<AppSettings xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<ReceiptLine1 />
<ReceiptLine2 />
<ReceiptLine3 />
<ReceiptLine4 />
<ReceiptLine5 />
<ReceiptLine6 />
<ReceiptLine1Enabled>false</ReceiptLine1Enabled>
<ReceiptLine2Enabled>true</ReceiptLine2Enabled>
<ReceiptLine3Enabled>false</ReceiptLine3Enabled>
<ReceiptLine4Enabled>false</ReceiptLine4Enabled>
<ReceiptLine5Enabled>false</ReceiptLine5Enabled>
<ReceiptLine6Enabled>false</ReceiptLine6Enabled>
<GatewayUserName>asdfasdf</GatewayUserName>
<GatewayPassword>asdf</GatewayPassword>
<GatewayId>sdf</GatewayId>
</AppSettings>>
Which it see two >> at the end.
Anyone see why it's saving two >> at the end of my xml file?
And my code errors out with:

It's because you're using File.OpenWrite:
For an existing file, it does not append the new text to the existing text. Instead, it overwrites the existing characters with the new characters. If you overwrite a longer string (such as “This is a test of the OpenWrite method”) with a shorter string (such as “Second run”), the file will contain a mix of the strings (“Second runtest of the OpenWrite method”).
While it's not clear from your example, I suspect the new contents are one byte shorter than the old contents, so you're seeing the closing angle bracket from the original file.
I suspect you should just use File.Create instead.

Related

Deserialize XML with duplicate nested tags

I'm deserialization the results of a request that has the same tag repeated at multiple levels, I have it working but to do so I'm changing the format of the XML before attempting to deserialize it.
I'm not able to edit the source of the XML to change it to only have Diary at one level.
Is there a way to adjust my XML attributes to handle the deserialization without needing to adjust the response?
XML Response
<?xml version="1.0" ?>
<Root>
<DiaryDetails>
<Diary>
<Diary created_user="value1" created_date="value2" long_text="value3" short_text="value4" entry_type="value5" >Value6</Diary>
</Diary>
<Diary>
<Diary created_user="value7" created_date="value8" long_text="value9" short_text="value10" entry_type="value11" >Value12</Diary>
</Diary>
</DiaryDetails>
</Root>
Class definition
[XmlRoot("DiaryDetails")]
public class Diaries : List<Diary> { }
[XmlRoot("Diary")]
public class Diary
{
[XmlAttribute("created_user")]
public string CreatedBy { get; set; }
[XmlAttribute("created_date")]
public string CreatedDate { get; set; }
[XmlAttribute("long_text")]
public string LongText { get; set; }
[XmlAttribute("short_text")]
public string ShortText { get; set; }
[XmlAttribute("entry_type")]
public string Type { get; set; }
}
Deserialization Method
internal T DeserilaiseObject<T>(string response)
{
XmlSerializer serializer = new XmlSerializer(typeof(T));
T DeserilaisedObject;
using (TextReader reader = new StringReader(response))
{
DeserilaisedObject = (T)serializer.Deserialize(reader);
}
return DeserilaisedObject;
}
I'm currently handling this with a string replace:
response = response.Replace("<Diary><Diary", "<Diary").Replace("</Diary></Diary>", "</Diary>");
Try following :
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml;
using System.Xml.Serialization;
namespace ConsoleApplication40
{
class Program
{
const string FILENAME = #"c:\temp\test.xml";
static void Main(string[] args)
{
XmlReader reader = XmlReader.Create(FILENAME);
XmlSerializer serializer = new XmlSerializer(typeof(Root));
Root root = (Root)serializer.Deserialize(reader);
}
}
[XmlRoot("Root")]
public class Root
{
[XmlArray("DiaryDetails")]
[XmlArrayItem("Diary")]
public List<DiaryMain> diaries { get; set; }
}
public class DiaryMain
{
public Diary Diary { get; set; }
}
[XmlRoot("Diary")]
public class Diary
{
[XmlAttribute("created_user")]
public string CreatedBy { get; set; }
[XmlAttribute("created_date")]
public string CreatedDate { get; set; }
[XmlAttribute("long_text")]
public string LongText { get; set; }
[XmlAttribute("short_text")]
public string ShortText { get; set; }
[XmlAttribute("entry_type")]
public string Type { get; set; }
}
}
Assuming that you are only interested at the attributed Diary elements at the second nesting level you could do this:
// load your xml into a document
var doc = new XmlDocument();
doc.Load("your_xml_file.xml");
// extract the interesting nodes using xpath
var nodes = doc.SelectNodes("//Diary/Diary");
// deserialize the filtered NodeList (yes it's that clunky)
var serializer = new XmlSerializer(typeof(Diary));
var diaries = nodes.Cast<XmlNode>().Select(x => serializer.Deserialize(new XmlNodeReader(x))).Cast<Diary>().ToArray();

XML Serializing information on Base class and inherited class

I am trying to serialize a C# object into XML so that it could be used as the body of API call. They are very particular about the input they need. I have built the following class to hold the data I need to send over to them. Including attributes and all properties as Elements instead of attributes. They also require that lists include the type="array" I though that creating my own class the implements a List would be the easiest since all lists I give them must have the same attribute. When serialization occurs it serializes the base class of List items but it doesn't include the attribute I want from the derived class.
public class CustomArray<T> : List<T>
{
[XmlAttribute]
public string type { get; set; } = "array";
}
[XmlRoot("message")]
public class MessageBody
{
[XmlArray("Checks"), XmlArrayItem("CheckItem")]
public CustomArray<Check> CheckList { get; set; }
}
public class Check
{
[XmlElement("C_CHECK_NUMBER")]
public string CheckNumber { get; set; }
[XmlElement("C_CHECK_AMOUNT")]
public decimal Amount { get; set; }
[XmlArray("InvoiceList"), XmlArrayItem("Invoice")]
public CustomArray<Invoice> InvoiceList { get; set; }
}
public class Invoice
{
[XmlElement("C_INVOICE_ID")]
public long ID { get; set; }
[XmlElement("C_INVOICE_NUM")]
public string InvoiceNum { get; set; }
}
I then run this code:
// Create a sample object
var message = new MessageBody()
{
CheckList = new CustomArray<Check>
{
new Check
{
CheckNumber = "111",
Amount = 1.00M
},
new Check
{
CheckNumber = "112",
Amount = 2.00M,
InvoiceList = new CustomArray<Invoice>
{
new Invoice
{
ID = 1,
InvoiceNum = "1"
}
}
}
}
};
// Create custom settings
var settings = new XmlWriterSettings
{
OmitXmlDeclaration = true,
Indent = true
};
// Serialize item and print it to console
using (var sw = new StringWriter())
using (var writer = XmlWriter.Create(sw, settings))
{
var serializer = new XmlSerializer(message.GetType());
serializer.Serialize(writer, message, new XmlSerializerNamespaces(new[] { XmlQualifiedName.Empty }));
Console.WriteLine(sw.ToString());
}
I get this written to the console:
<message>
<Checks>
<CheckItem>
<C_CHECK_NUMBER>111</C_CHECK_NUMBER>
<C_CHECK_AMOUNT>1.00</C_CHECK_AMOUNT>
</CheckItem>
<CheckItem>
<C_CHECK_NUMBER>112</C_CHECK_NUMBER>
<C_CHECK_AMOUNT>2.00</C_CHECK_AMOUNT>
<InvoiceList>
<Invoice>
<C_INVOICE_ID>1</C_INVOICE_ID>
<C_INVOICE_NUM>1</C_INVOICE_NUM>
</Invoice>
</InvoiceList>
</CheckItem>
</Checks>
</message>
But I need to get this:
<message>
<Checks type="array">
<CheckItem>
<C_CHECK_NUMBER>111</C_CHECK_NUMBER>
<C_CHECK_AMOUNT>1.00</C_CHECK_AMOUNT>
</CheckItem>
<CheckItem>
<C_CHECK_NUMBER>112</C_CHECK_NUMBER>
<C_CHECK_AMOUNT>2.00</C_CHECK_AMOUNT>
<InvoiceList type="array">
<Invoice>
<C_INVOICE_ID>1</C_INVOICE_ID>
<C_INVOICE_NUM>1</C_INVOICE_NUM>
</Invoice>
</InvoiceList>
</CheckItem>
</Checks>
</message>
Thank you for your help!
Here is a dotnetfiddle that I made to show it off. It's not exact but it has the same idea. https://dotnetfiddle.net/ALCX5H
Try following :
[XmlRoot("message")]
public class MessageBody
{
[XmlElement("Checks")]
public Checks Checks { get; set; }
}
public class Checks
{
[XmlAttribute]
public string type { get; set; }
[XmlElement("Checks")]
public List<Check> Checks { get; set; }
}
public class Check
{
[XmlElement("C_CHECK_NUMBER")]
public string CheckNumber { get; set; }
[XmlElement("C_CHECK_AMOUNT")]
public decimal Amount { get; set; }
}

xml serialiser with lists and other objects

I've been looking here for a sollution, but I did not find it in previous answers.
I need to create the following xml with the serialiser
<?xml version="1.0" encoding="Windows-1252"?>
<documents xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<document>
<Keys>
<drawer>GraphicData</drawer>
<somedata>otherData</somedata>
</Keys>
<otherGenericProperties>
<Data>GenericData 2</Data>
</otherGenericProperties>
<Repeat>
<FileInfo mimeType="application/pdf" HREF="PdfFile.pdf" />
</Repeat>
<Repeat>
<FileInfo mimeType="application/pdf" HREF="PdfFile2.pdf" />
</Repeat>
</document>
</documents>
The data consist of a few classes
namespace test
{
public class Documents
{
[XmlElement("Keys")] // this is used so the destination name can change in the future
public Keys keys { get; set; }
}
public class document
{
[XmlElement("Keys")] // this is used so the destination name can change in the future
public Keys keys { get; set; }
[XmlElement("Repeat")] // This does not work
List<Repeat> listRepeat { get; set; }
}
public class Keys
{
[XmlElement("drawer")]
public string drawer { get; set; }
}
public class Repeat
{
[XmlElement("fileInfo")]
public FileInfo fileInfo { get; set; }
}
public class FileInfo
{
public string FileInfo { get; set; }
[XmlAttribute("mimeType")]
public string mimeType { get; set; }
[XmlAttribute("mimeType")]
public string HREF { get; set; }
}
}
the serialiser :
XmlSerializer serializer = new XmlSerializer(typeof(Documents));
using (StreamWriter writer = new StreamWriter(saveBestand, false, xmlEncoding))
{
serializer.Serialize(writer, icm, namespaces);
}
I really need the xm like the example, and also the xml names should be changable by a contract like xmlElement is used for. Somehow the repeat element cant be placed on the level you see in the example. Does anyone have a sollution ?
you have two options.
Option 1
use xsd.exe to generate classes using the xml you want.
xsd yourxmlfile.xml
xsd yourxmlfile.xsd /classes
This will generate classes which you can use for serialization or deserialization.
Option 2:
Paste special option to generate the c# classes.
This does not involve using the command line.
refer blog at: https://dennymichael.net/2014/05/30/convert-xml-to-csharp-classes/comment-page-1/
It seems that you poorly designed your data classes. Try solution below.
public class OtherGenericProperties
{
[XmlElement("Data")]
public string Data { get; set; }
}
[XmlRoot("documents")]
public class Documents
{
[XmlElement("document")]
public Document Document { get; set; }
}
public class Document
{
[XmlElement("Keys")] // this is used so the destination name can change in the future
public Keys Keys { get; set; }
[XmlElement("otherGenericProperties")]
public OtherGenericProperties OtherGenericProperties { get; set; }
[XmlElement("Repeat")] // This does not work
public List<Repeat> ListRepeat { get; set; }
}
public class Keys
{
[XmlElement("drawer")]
public string Drawer { get; set; }
[XmlElement("somedata")]
public string SomeData { get; set; }
}
public class Repeat
{
[XmlElement("FileInfo")]
public FileInfo FileInfo { get; set; }
}
public class FileInfo
{
[XmlAttribute("mimeType")]
public string MimeType { get; set; }
[XmlAttribute("HREF")]
public string Href { get; set; }
}
Serializing:
internal static void Test()
{
var doc = new Documents
{
Document = new Document
{
Keys = new Keys
{
Drawer = "GraphicData",
SomeData = "otherData"
},
OtherGenericProperties = new OtherGenericProperties { Data = "GenericData 2" },
ListRepeat = new List<Repeat>
{
new Repeat { FileInfo =new FileInfo { Href = "PdfFile.pdf", MimeType = "application/pdf" } },
new Repeat { FileInfo = new FileInfo { Href = "PdfFile2.pdf", MimeType = "application/pdf" } }
}
}
};
XmlSerializer serializer = new XmlSerializer(typeof(Documents));
using (var f = new StreamWriter("D:\\doc.xml", false, Encoding.GetEncoding("Windows-1252")))
{
serializer.Serialize(f, doc);
f.Flush();
}
}
Output:
<?xml version="1.0" encoding="Windows-1252"?>
<documents xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<document>
<Keys>
<drawer>GraphicData</drawer>
<somedata>otherData</somedata>
</Keys>
<otherGenericProperties>
<Data>GenericData 2</Data>
</otherGenericProperties>
<Repeat>
<FileInfo mimeType="application/pdf" HREF="PdfFile.pdf" />
</Repeat>
<Repeat>
<FileInfo mimeType="application/pdf" HREF="PdfFile2.pdf" />
</Repeat>
</document>
</documents>

input string was not in a correct format when deserialize xml

I have a weird problem. i have this Xml
<?xml version="1.0" encoding="utf-8"?>
<Facilities RunTime="2016-03-09 14:18:11">
<Facility ID="789">
<Name>Facility 4</Name>
<Contact />
<AreaName>Center</AreaName>
<MunicipalCode>453</MunicipalCode>
<SMSInfo />
<Materials />
</Facility>
<Facility ID="-1">
<Name>Facility 2</Name>
<Contact />
<AreaName>Mark</AreaName>
<MunicipalCode />
<SMSInfo />
<Materials />
</Facility>
</Facilities>
When i try to deserialize this xml, it fails on on tag <MunicipalCode /> when its empty (see the element MunicipalCode in the second root Facility)
but not on <MunicipalCode>453</MunicipalCode> (in the first root) so when i change the empty one to <MunicipalCode>test test </MunicipalCode> then it doesnt fail
this is my model, and i have tried to handle this value in case it comes as null.
[Table("FacilityNew")]
public class FacilityNew
{
[XmlAttribute("ID"), Key]
public int Id { get; set; }
[XmlElement("SMSInfo")]
public virtual SMSInfo smsInfos { get; set; }
[XmlElement("Name")]
public string Name { get; set; }
[XmlElement("Contact")]
public string Contact { get; set; }
[XmlElement("AreaName")]
public string AreaNameID { get; set; }
[XmlIgnore]
public string MunicipalCode { get; set; }
[XmlElement("MunicipalCode")]
[Browsable(false)] // not displayed in grids
[EditorBrowsable(EditorBrowsableState.Never)] // not displayed by intellisense
public string MunicipalCodeStirng
{
get
{
if ((MunicipalCode) != null)
{
return MunicipalCode;
}
else
return "";
}
set
{
if ((value)!=null)
{
MunicipalCode = value;
}
else
{
MunicipalCode = "";
}
}
}
[XmlArray("Materials")]
[XmlArrayItem("Material")]
public virtual List<Material> Materials { get; set; }
public FacilityNew()
{
this.Materials = new List<Material>();
}
}
but it still fails
its weird because the other empty tags doesn't fail, and i got "input string was not in a correct format " and if i change the name of this tag to <MunicipalCodeASDF /> or something else, it doesn't fail.
this is how i deserialize,
XmlSerializer deserializer = new XmlSerializer(typeof(FacilityNew));
StreamReader sr = new StreamReader(path);
allaFacilities = (FacilityNew)deserializer.Deserialize(sr);
what is the problem
the problem is solved by adding a regex inside the deserializer
public static T Deserialize<T>(string xml){
XmlSerializer xs = new XmlSerializer(typeof(T));
string cleanXml = Regex.Replace(xml, #"<[a-zA-Z].[^(><.)]+/>",
new MatchEvaluator(RemoveText));
MemoryStream memoryStream = new MemoryStream((new UTF8Encoding()).GetBytes(cleanXml));
XmlTextWriter xmlTextWriter = new XmlTextWriter(memoryStream, Encoding.UTF8);
return (T)xs.Deserialize(memoryStream);
}
static string RemoveText(Match m) { return "";}

Deserialize XML List<MyType>

Right now I have a XML file which looks like this :
<?xml version="1.0" encoding="utf-8"?>
<ArrayOfMyMedia xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<MyMedia>
<fileName>C:\Users\Yanis\Pictures\Photos\VID_20130704_203538.mp4</fileName>
<title>VID_20130704_203538.mp4</title>
<length>0</length>
<_type>0</_type>
</MyMedia>
<MyMedia>
<fileName>C:\Users\Yanis\Pictures\Photos\VID_20130802_142038.mp4</fileName>
<title>VID_20130802_142038.mp4</title>
<length>0</length>
<_type>0</_type>
</MyMedia>
</ArrayOfMyMedia>
This XML is generated by this function :
public void WriteXML(List<MyMedia> myMedias)
{
System.Xml.Serialization.XmlSerializer writer = new System.Xml.Serialization.XmlSerializer(typeof(List<MyMedia>));
System.IO.StreamWriter file = new System.IO.StreamWriter(
#"c:\temp\SerializationOverview.xml");
writer.Serialize(file, myMedias);
file.Close();
}
And when I want to retrieve my list of medias of type : List<MyMedias>
With this function :
public void LoadXML()
{
System.Xml.Serialization.XmlSerializer reader = new System.Xml.Serialization.XmlSerializer(typeof(List<MyMedia>));
System.IO.StreamReader file = new System.IO.StreamReader(#"c:\temp\SerializationOverview.xml");
window1.medias = (List<MyMedia>)reader.Deserialize(file);
for (int i = 0; i < window1.medias.Count; i++)
{
window1.mediaList.Items.Add(window1.medias[i]);
}
}
Here is my "MyMedia" class, it's pretty simple though :
public class MyMedia
{
enum type
{
MUSIC,
VIDEO,
IMAGE
};
public String fileName { get; set; }
public String artist { get; set; }
public String title { get; set; }
public int length { get; set; }
public int _type { get; set; }
public MyMedia()
{
}
public override string ToString()
{
return title;
}
}
My window1.medias variable is empty.
Any clue ?
Edit : I found the solution, I had a field in my "MyMedia" class that was not in my .xml file. I removed it and it worked.

Categories