C# XmlSerializer Add an Attribute to a Field - c#

I want to add a custom Attribute belonging to a field
Goal is to get the following XML:
<?xml version="1.0"?>
<doc>
<assembly>
<name>class1</name>
</assembly>
<members>
<member name="P:class1.clsPerson.isAlive">
<Element>
isAlive
</Element>
<Description>
Whether the object is alive or dead
</Description>
<StandardValue>
false
</StandardValue>
</member>
</members>
</doc>
What I currently have:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Xml.Serialization;
namespace class1
{
public class clsPerson
{
[XmlElement(ElementName="isAlive")]
[Description("Whether the object is alive or dead")]
[StandardValue(false)]
public bool isAlive { get; set; }
}
class Program
{
static void Main(string[] args)
{
clsPerson p = new clsPerson();
p.isAlive = true;
System.Xml.Serialization.XmlSerializer x = new System.Xml.Serialization.XmlSerializer(p.GetType());
x.Serialize(Console.Out, p);
Console.WriteLine();
Console.ReadLine();
}
}
}
My current Annotation classes:
using System;
namespace class1
{
internal class StandardValueAttribute : Attribute
{
public readonly object DefaultValue;
public StandardValueAttribute(Object defaultValue)
{
this.DefaultValue = defaultValue;
}
}
}
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace class1
{
internal class DescriptionAttribute : Attribute
{
private string v;
public DescriptionAttribute(string v)
{
this.v = v;
}
}
}
How can I add my custom Attributes like Description and StandardValue to the XMLSerializer?

It looks like you want to reinvent the wheel. if you trying to export a code documentation I suggest you using the built in functionality:
https://msdn.microsoft.com/en-us/library/b2s063f7.aspx
The XML documentation files are then generated and you can even use them in intellisense
The XMLSerializer will just store the content of an instance.

Using xml Linq
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml;
using System.Xml.Linq;
namespace ConsoleApplication14
{
class Program
{
const string FILENAME = #"c:\temp\test.xml";
static void Main(string[] args)
{
XDocument doc = XDocument.Load(FILENAME);
string name = "P:class1.clsPerson.isAlive";
XElement person = doc.Descendants("member").Where(x => (string)x.Attribute("name") == name).FirstOrDefault();
person.Add(new object[] {
new XElement("Description", "Whether the object is alive or dead"),
new XElement("StandardValue", false)
});
}
}
}

Related

Can't Deserialize xml using DataContractSerializer

I can't deserialize this XML to an object, I don't know what is wrong with this:
<s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/">
<s:Body>
<ProcessOneWayEvent xmlns="http://schemas.microsoft.com/sharepoint/remoteapp/">
<properties xmlns:i="http://www.w3.org/2001/XMLSchema-instance">
<CultureLCID>1033</CultureLCID>
</properties>
</ProcessOneWayEvent>
</s:Body>
</s:Envelope>
Here is a ready sample for this, is there any workaround, because I can't modify in the request, so is there any thing wrong with my models? And is there any solution to deserialize the XML without using XmlSerializer
https://dotnetfiddle.net/RfQMSD
Body and SPRemoteEventProperties need to be in the "http://schemas.microsoft.com/sharepoint/remoteapp/" namespace:
[DataContract(Name = "Body", Namespace = "http://schemas.microsoft.com/sharepoint/remoteapp/")]
public class Body
{
[DataMember(Name = "ProcessOneWayEvent")]
public ProcessOneWayEvent ProcessOneWayEvent;
}
[DataContract(Name = "properties", Namespace = "http://schemas.microsoft.com/sharepoint/remoteapp/")]
public class SPRemoteEventProperties
{
[DataMember(Name = "CultureLCID") ]
public int CultureLCID { get; set; }
}
The DataContractAttribute.Namespace controls the namespace that the data contract object's data member elements are serialized to, as well as the namespace of the root element when the data contract object is the root element. Since the element <ProcessOneWayEvent xmlns="http://schemas.microsoft.com/sharepoint/remoteapp/"> declares a new default XML namespace, the element itself, as well as its children, are in this namespace. Thus, the containing data contract object Body must set its data member namespace accordingly. As for <properties xmlns:i="http://www.w3.org/2001/XMLSchema-instance">, the i: namespace is not a default namespace, so no elements actually get assigned to this namespace and the SPRemoteEventProperties type should not not assign itself to it.
Fixed fiddle here.
I don't immediately see why it doesn't work, but one alternative is to use System.Xml.Serialization.XmlSerializer.
Start by creating the appropriate Envelope classes by copying the XML into a new class (Edit → Paste Special → Paste XML as Classes)
Then deserialize using this as an example:
byte[] byteArray = System.Text.Encoding.UTF8.GetBytes(request);
using (var stream = new MemoryStream(byteArray))
{
var serializer = new XmlSerializer(typeof(Envelope));
Envelope response = (Envelope)serializer.Deserialize(stream);
Console.WriteLine(JsonConvert.SerializeObject(response));
}
Try following :
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml;
using System.Xml.Serialization;
using System.IO;
namespace ConsoleApplication1
{
class Program
{
const string FILENAME = #"c:\temp\test.xml";
static void Main(string[] args)
{
string xml = File.ReadAllText(FILENAME);
StringReader sReader = new StringReader(xml);
XmlReader reader = XmlReader.Create(sReader);
XmlSerializer serializer = new XmlSerializer(typeof(Envelope));
Envelope envelope = (Envelope)serializer.Deserialize(reader);
}
}
[XmlRoot(ElementName = "Envelope", Namespace = "http://schemas.xmlsoap.org/soap/envelope/")]
public class Envelope
{
[XmlElement(ElementName = "Body", Namespace = "http://schemas.xmlsoap.org/soap/envelope/")]
public Body Body { get; set; }
}
public class Body
{
[XmlElement(ElementName = "ProcessOneWayEvent", Namespace = "http://schemas.microsoft.com/sharepoint/remoteapp/")]
public ProcessOneWayEvent ProcessOneWayEvent { get; set; }
}
public class ProcessOneWayEvent
{
public Properties properties { get; set; }
}
public class Properties
{
public string CultureLCID { get; set; }
}
}
Using Xml Linq
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml;
using System.Xml.Linq;
using System.IO;
namespace ConsoleApplication1
{
class Program
{
const string FILENAME = #"c:\temp\test.xml";
static void Main(string[] args)
{
string xml = File.ReadAllText(FILENAME);
XDocument doc = XDocument.Parse(xml);
string CultureLCID = (string)doc.Descendants().Where(x => x.Name.LocalName == "CultureLCID").FirstOrDefault();
}
}
}

How to get the current attribute value in Xml and using the current attribute value, retrieve the next node value?

<?xml version="1.0" encoding="utf-8" ?>
<questions>
<question num="1">Employees should be involved in setting their goals.</question>
<question num="2">Most people resists change.</question>
<question num="3">Manager should guide rather control.</question>
<question num="4">Average person is easily decieved.</question>
</questions>
How can I set a current attribute value to a variable i, using this current attribute i when a method next() is called, it should retrieve next node value (for example if current attribute value is 1 when the next() is called it should retrieve 2nd node value and when again next() is called it should retrieve 3rd attribute value) using System.Xml?
I have tried to retrieve the first node value using Xpath, but I have no idea how to set the current attribute value to a variable and then using the variable moving to the next node value. The following is the code I have tried.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Xml;
using System.Xml.XPath;
namespace ConsoleApplication14
{
interface IQuestion
{
void Question1();
void Next();
}
class Question : IQuestion
{
string BuildXpathQuery(int c)
{
string part1 = #"questions/question[#num='";
string part2 = #"']";
string MyQuery = part1 + c + part2;
return MyQuery;
}
public void Question1()
{
XmlDocument doc = new XmlDocument();
doc.Load("C:\\Users\\Murari\\Documents\\Visual Studio 2015\\Projects\\ConsoleApplication14\\ConsoleApplication14\\XMLFile1.xml");
XmlNode xnList = doc.SelectSingleNode(BuildXpathQuery(1));
if (xnList != null)
{
Console.WriteLine(xnList.InnerXml);
Console.ReadKey();
}
}
public void Next()
{
}
}
class Program
{
static void Main(string[] args)
{
IQuestion ques = new Question();
ques.Question1();
ques.Next();
}
}
}
You simple need to create a a List<> object. Using XML Linq is best way
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);
var xnList = doc.Descendants("question").Select(x => new
{
num = (int)x.Attribute("num"),
text = x.Value
}).ToList();
foreach (var question in xnList)
{
}
}
}
}

Json.NET Not serializing to file

Even using an example from the Documentation, I still can't find a way to successfully serialize to a file.
Code:
Program.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Newtonsoft.Json;
using System.IO;
using System.Threading;
namespace TestProject
{
class Program
{
static void Main(string[] args)
{
Item i = new Item
{
Username = "user",
Email = "user#user.com",
Password = "password"
};
File.WriteAllText(#"C:\users\user1.json", JsonConvert.SerializeObject(i));
using (StreamWriter file = File.CreateText(#"C:\users\user1.json"))
{
JsonSerializer serializer = new JsonSerializer();
serializer.Serialize(file, i);
}
}
}
}
Item.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace TestProject
{
public class Item
{
public string Email { get; set; }
public string Password { get; set; }
public string Username { get; set; }
}
}
When I run Program.cs, it shows no errors, but the JSON does not show in the file.
When I run your code I get a an exception writing to the users folder. Changing to my home folder works fine. I suspect this is your problem.
Addtionally, you're writing the file twice. The first time is showing an example of using the SerializeObject method to get a string back which is used with WriteAllText. The second block of code is using a StreamWriter. For your purposes, both are equivalent and you only need to use one or the other.
using (StreamWriter file = File.CreateText(#"C:\users\user1.json"))
{
var jsonResult = JsonConvert.SerializeObject(i);
file.WriteLine(jsonResult);
}

Different behavior in XmlSerializer with/without XmlElement

using System;
using System.Collections.Generic;
using System.Text;
using System.Diagnostics;
using System.IO;
using System.Xml;
using System.Xml.Linq;
using System.Xml.Serialization;
using System.Linq;
namespace Serialize
{
public class Good
{
public int a;
public Good() {}
public Good(int x)
{
a = x;
}
}
public class Hello
{
public int x;
public List<Good> goods = new List<Good>();
public Hello()
{
goods.Add(new Good(1));
goods.Add(new Good(2));
}
}
[XmlRootAttribute("Component", IsNullable = false)]
public class Component {
//[XmlElement("worlds_wola", IsNullable = false)]
public List<Hello> worlds;
public Component()
{
worlds = new List<Hello>() {new Hello(), new Hello()};
}
}
class Cov2xml
{
static void Main(string[] args)
{
string xmlFileName = "ip-xact.xml";
Component comp = new Component();
TextWriter writeFileStream = new StreamWriter(xmlFileName);
var ser = new XmlSerializer(typeof(Component));
ser.Serialize(writeFileStream, comp);
writeFileStream.Close();
}
}
}
With this XmlSerializer code, I get this XML file.
I have only one "worlds" element, that has two Hello elements.
However, when I add XmlElement before the worlds varibale.
[XmlElement("worlds_wola", IsNullable = false)]
public List<Hello> worlds
I have two worlds_wola elements instead of one.
Why is this? How can I use XmlElement to specify the name of the tag, but with only one "worlds_wola" element as follows?
<worlds_wola>
<Hello>
...
</Hello>
<Hello>
...
</Hello>
</worlds_wola>
You need to use the XmlArrayAttribute for your collection instead of the XmlElementAttribute.
I found that this is exactly what I wanted based on Charles' answer.
[XmlArray("fileSet")]
[XmlArrayItem(ElementName = "file", IsNullable = false)]
public List<Hello> worlds;
With this setup, I could get
<fileSet>
<file>...</file>
Instead of
<worlds>
<Hello>...</Hello>

How to change XML root name with XML Serialization?

I am trying to change the root name when doing XML serialization with C#.
It always takes the class name and not the name I am trying to set it with.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml.Serialization;
using System.IO;
namespace ConsoleApplication1
{
class Program
{
static void Main(string[] args)
{
MyTest test = new MyTest();
test.Test = "gog";
List<MyTest> testList = new List<MyTest>()
{
test
};
SerializeToXML(testList);
}
static public void SerializeToXML(List<MyTest> list)
{
XmlSerializer serializer = new XmlSerializer(typeof(List<MyTest>));
TextWriter textWriter = new StreamWriter(#"C:\New folder\test.xml");
serializer.Serialize(textWriter, list);
textWriter.Close();
}
}
}
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml.Serialization;
namespace ConsoleApplication1
{
[XmlRootAttribute(ElementName = "WildAnimal", IsNullable = false)]
public class MyTest
{
[XmlElement("Test")]
public string Test { get; set; }
}
}
Result
<?xml version="1.0" encoding="utf-8"?>
<ArrayOfMyTest xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<MyTest>
<Test>gog</Test>
</MyTest>
</ArrayOfMyTest>
It does not change it to WildAnimal. I am not sure why. I got this from a tutorial.
Edit # Marc
Thanks. I now see what your doing just seems so odd that you have to make a wrapper around it. I have one more question what happens if I wanted to make this format
<root>
<element>
<name></name>
</element>
<anotherElement>
<product></product>
</anotherElement>
</root>
so like a nested elements. Would I have to make a new class for the second part and stick that in the wrapper class too?
In your example, MyTest is not the root; are you trying to rename the array? I would write a wrapper:
[XmlRoot("NameOfRootElement")]
public class MyWrapper {
private List<MyTest> items = new List<MyTest>();
[XmlElement("NameOfChildElement")]
public List<MyTest> Items { get { return items; } }
}
static void Main() {
MyTest test = new MyTest();
test.Test = "gog";
MyWrapper wrapper = new MyWrapper {
Items = { test }
};
SerializeToXML(wrapper);
}
static public void SerializeToXML(MyWrapper list) {
XmlSerializer serializer = new XmlSerializer(typeof(MyWrapper));
using (TextWriter textWriter = new StreamWriter(#"test.xml")) {
serializer.Serialize(textWriter, list);
textWriter.Close();
}
}

Categories