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>
Related
Trying to create an XML file of the following structure:
<Objects>
<ArrayOfObject1>
<Object1>
<Object1>
</ArrayOfObject1>
<ArrayOfObject2>
<Object2>
<Object2>
</ArrayOfObject2>
</Objects>
Is there any way to achieve this using the XmlSerializer? I'm aware that the following code snippet can be used to generate an XML file that has a root node of ArrayOfObject1 and elements corresponding to every object in the list; I'd just like to extend this to have a different root node, and an array of each object within it.
XmlSerializer serializer = new XmlSerializer(typeof(List<Object1>));
Of course, it may be best practice to have two different XML files, but I'm just trying to find out if there's an acceptable/sensible way to have two arrays of objects represented in a single file.
Thanks!
Try following :
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml;
using System.Xml.Serialization;
namespace ConsoleApplication2
{
class Program
{
const string FILENAME = #"c:\temp\test.xml";
static void Main(string[] args)
{
XmlWriterSettings settings = new XmlWriterSettings();
settings.Indent = true;
XmlWriter writer = XmlWriter.Create(FILENAME, settings);
XmlSerializer serializer = new XmlSerializer(typeof(Objects));
Objects objects = new Objects()
{
objects1 = new List<Object1>()
{
new Object1(), new Object1()
},
objects2 = new List<Object2>()
{
new Object2(), new Object2()
}
};
serializer.Serialize(writer, objects);
}
}
public class Objects
{
[XmlArray("ArrayOfObject1")]
[XmlArrayItem("Object1")]
public List<Object1> objects1 { get; set; }
[XmlArray("ArrayOfObject2")]
[XmlArrayItem("Object2")]
public List<Object2> objects2 { get; set; }
}
public class Object1
{
}
public class Object2
{
}
}
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();
}
}
}
Im trying to write a C# application that parses an XML file and returns the selected node through a function. These are my classes:
XmlDocumentParser.cs
using System;
using System.Text;
using System.IO;
using System.Xml;
namespace Namespace1
{
public class XmlDocumentParser
{
// This is a private instance variable that cannot be reassigned
private readonly XmlDocument document = new XmlDocument();
private string udc = "urn:rosettanet:specification:universal:Document:xsd:schema:01.12";
private string udct = "urn:rosettanet:specification:universal:DocumentType:xsd:codelist:01.13";
private string tns = "urn:rosettanet:specification:interchange:PurchaseOrderRequest:xsd:schema:02.05";
private string dp = "urn:rosettanet:specification:domain:Procurement:xsd:schema:02.29";
//This is the constructor
public XmlDocumentParser(string documentPath)
{
document.Load(documentPath);
XmlNode Root = document.DocumentElement;
XmlNamespaceManager Nsmgr = new XmlNamespaceManager(document.NameTable);
Nsmgr.AddNamespace("udc", udc);
Nsmgr.AddNamespace("udct", udct);
Nsmgr.AddNamespace("tns", tns);
}
// This is a public property that can only be read and not set
public XmlNode Root { private set; get; }
public XmlNode Nsmgr {private set; get; }
}
}
PORReader.cs
using System;
using System.Text;
using System.IO;
using System.Xml;
namespace Namespace1 {
public class PORReader {
private readonly XmlNode root;
private readonly XmlNamespaceManager nsmgr;
public PORReader(string documentPath){
var xmlDocumentParser = new XmlDocumentParser(documentPath);
var root = xmlDocumentParser.Root;
}
public object getPurchaseOrder(){
XmlNode node = root.SelectSingleNode(
"descendant::tns:PurchaseOrder/udc:BusinessDocumentReference[udct:DocumentType='SAO']/udc:Identifier", nsmgr);
return returnNode(node.InnerXml);
}
private object returnNode(object node){
if (node != null) {
return node;
} else {
return "Error in Node";
}
}
}
}
Program.cs:
using System;
using System.Text;
using System.IO;
using System.Xml;
namespace Namespace1
{
class Program
{
static void Main(string[] args){
// This will reside in the reader classes
var xmlDocumentParser = new XmlDocumentParser("/Users/moorel/Desktop/Projects/C#/O2/DummyFiles/SupplyChainSourceFiles/POR/POR_SALES_8307_20180201164154.xml");
var root = xmlDocumentParser.Root;
var nsmgr = xmlDocumentParser.Nsmgr;
}
}
}
And my test is:
namespace Namespace1 {
using NUnit.Framework;
[TestFixture]
public class PORReaderTest {
[Test]
public void getPurchaseOrder(){
PORReader reader = new PORReader("/Users/moorel/Desktop/Projects/C#/O2/DummyFiles/SupplyChainSourceFiles/POR/POR_SALES_8307_20180201164154.xml");
Assert.AreEqual(reader.getPurchaseOrder(), "aa036E31tc63qjPfIMJ");
}
}
}
So, the document path to the xml is correct and the value ""aa036E31tc63qjPfIMJ" is present in the xml, however the test is erroring with the message System.NullReferenceException : Object reference not set to an instance of an object.
In class PORReader the value of root is null, and I need it to be the object thats imported from XmlDocumentParser. I'm unfamiliar with C# classes so any help would be much appreciated
Thanks!
Oh I see.
You're not acutally doing anything in the XmlDocumentParser constructor.
This line:
XmlNode Root = document.DocumentElement;
does not assign to the Root property, it creates a local variable named Root. Change it to
Root = document.DocumentElement;
and see if that helps.
Also it would probably be wise to make a run with the debugger and see if everythings correct, e.g. if document.DocumentElement actually returns the root you're looking for.
EDIT:
I'm blind.
You have the exact same error in PORReader.
var root = xmlDocumentParser.Root;
This doesn't assign to your private readonly field, but creates a local variable. Change to:
root = xmlDocumentParser.Root;
XmlDocumentParser.cs
namespace Namespace1
{
public class XmlDocumentParser
{
// This is a private instance variable that cannot be reassigned
private readonly XmlDocument document = new XmlDocument();
private string udc = "urn:rosettanet:specification:universal:Document:xsd:schema:01.12";
private string udct = "urn:rosettanet:specification:universal:DocumentType:xsd:codelist:01.13";
private string tns = "urn:rosettanet:specification:interchange:PurchaseOrderRequest:xsd:schema:02.05";
private string dp = "urn:rosettanet:specification:domain:Procurement:xsd:schema:02.29";
//This is the constructor
public XmlDocumentParser(string documentPath)
{
document.Load(documentPath);
Root = document.DocumentElement as XmlNode;
Nsmgr = new XmlNamespaceManager(document.NameTable);
Nsmgr.AddNamespace("udc", udc);
Nsmgr.AddNamespace("udct", udct);
Nsmgr.AddNamespace("tns", tns);
}
// This is a public property that can only be read and not set
public XmlNode Root { get; }
public XmlNamespaceManager Nsmgr { get; }
}
}
I have an XML document I'm trying to deseralize that has a attribute that is ref which in C# can not be used to declare a variable hence the below doesn't work
[XmlAttribute()]
public string ref;
Anyway to get this to deseralize properly? I know it is case sensitive so Ref wouldn't work.
You can provide a name in the attribute:
[XmlAttribute("ref")]
public string anynameyouwant;
You can change the attribute name in the xml file, by using the AttributeName, such as in the following example:
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Xml.Serialization;
namespace soans
{
public class Test
{
//problematic attribute (ref is reserved)
[XmlAttribute(AttributeName="ref")]
public string RefAttr {get;set;}
//other attributes as well
[XmlAttribute()]
public string Field { get; set; }
}
class Program
{
static void Main(string[] args)
{
string filename = ""; //use your path here
Test original = new Test()
{
RefAttr = "ref",
Field = "test"
};
//serialiser
XmlSerializer ser = new XmlSerializer(typeof(Test));
//save to file
TextWriter writer = new StreamWriter(filename);
ser.Serialize(writer, original);
writer.Close();
//read from file
TextReader reader = new StreamReader(filename);
var fromfile = ser.Deserialize(reader) as Test;
if(fromfile!=null)
{
Console.WriteLine(fromfile.RefAttr);
}
reader.Close();
Console.ReadKey();
}
}
}
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();
}
}