C# passing object between class is null - c#

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; }
}
}

Related

How to add namespaces to XML before serialization

I do have a problem with serialization of a namespaces. As my code show below my logic fills the structure of my dictionary, serializes it and puts into a string variable. I use this variable to load into XMLDocument and after that I do add the namespaces. But since they are added after the serialization process the namespaces are set in UTF8? Should I add namespaces before serialization? If yes, how can I do it properly?
///Dictionary
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Xml.Serialization;
namespace TestXML
{
[Serializable]
[XmlRoot(ElementName = "Root")]
public class XMLSchema: Serialization
{
[XmlElement(ElementName = "Element1")]
public XMLElement1 Element1 { get; set; }
[XmlElement(ElementName = "Element2")]
public XMLElement2 Element2 { get; set; }
}
}
///Serialization class
public class Utf8StringWriter : StringWriter
{
// Use UTF8 encoding
public override Encoding Encoding
{
get { return new UTF8Encoding(false); }
}
}
public string Serialize()
{
var xmlserializer = new XmlSerializer(this.GetType());
var Utf8StringWriter = new Utf8StringWriter();
var xns = new XmlSerializerNamespaces();
xns.Add(string.Empty, string.Empty);
using (var writer = XmlWriter.Create(Utf8StringWriter))
{
xmlserializer.Serialize(writer, this, xns);
return Utf8StringWriter.ToString();
}
}
///create xml
str xml;
[...] my logic to add data into elements
XmlDocument doc = new XmlDocument();
xml = XMLSchema.Serialize();
doc.LoadXml(xml);
XmlElement root = doc.getNamedElement("Root");
root.SetAttribute("xmlns:etd", "http://google.com");
root.SetAttribute("xmlns:xsi", "http://google.com");
root.SetAttribute("xmlns", "http://google.com");
doc.AppendChild(root);
doc.Save(path);
Edit. Adding provided sample.
<?xml version="1.0" encoding="UTF-8"?> <XMLSample xmlns="http://crd.gov.pl/wzor/" xmlns:xsi="http://www.w3.org/2001/" xmlns:etd="http://crd.gov.pl/xml/schematy/">
So to give you an example (based on your sample):
[XmlRoot("XMLSample", Namespace = "http://crd.gov.pl/wzor/")]
public class XmlSample
{
[XmlElement]
public string Element1 { get; set; }
[XmlElement(Namespace = "http://crd.gov.pl/xml/schematy/")]
public string Element2 { get; set; }
}
Here, the root has the namespace http://crd.gov.pl/wzor/. Element1 inherits that namespace (as none is specified). Element2 has the namespace http://crd.gov.pl/xml/schematy/.
When this is serialised, the serialiser will use the root namespace as the default, so there's no need to explicitly set this. You can set the others to use the prefixes as defined in your sample:
var xsn = new XmlSerializerNamespaces();
xsn.Add("xsi", "http://www.w3.org/2001/");
xsn.Add("etd", "http://crd.gov.pl/xml/schematy/");
You can see this fiddle for a demo, the output is:
<?xml version="1.0" encoding="utf-8"?>
<XMLSample xmlns:xsi="http://www.w3.org/2001/" xmlns:etd="http://crd.gov.pl/xml/schematy/" xmlns="http://crd.gov.pl/wzor/">
<Element1>foo</Element1>
<etd:Element2>bar</etd:Element2>
</XMLSample>
Note that Element2 uses the prefix configured for its namespace.

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();
}
}
}

static Xml Deserialization into a generic list in C# not working

So I have an XML file that I am trying to deserialize into a generic list, but no matter what I do, the List stays empty. I went threw the first ~10 Stack overflow questions that popped up about this, but I haven't figured it out yet.
When the program starts up, I call FishContainer.Load(), the file reader reads the file just fine, but nothing goes in the List.
Xml File
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<FishContainer>
<Fishies>
<Fish Name = "test">
<WaterType>salt</WaterType>
<Price>1</Price>
<Size>1</Size>
<Aggression>1</Aggression>
</Fish>
<Fish Name = "test2">
<WaterType>fresh</WaterType>
<Price>12</Price>
<Size>12</Size>
<Aggression>12</Aggression>
</Fish>
</Fishies>
</FishContainer>
Fish Container
using UnityEngine;
using System.Collections.Generic;
using System.Xml.Serialization;
using System.IO;
public static class FishContainer {
[XmlArray("Fishies"), XmlArrayItem("Fish")]
public static List<FishData> fishs = new List<FishData>();
public static void Load() {
TextAsset _xml = Resources.Load<TextAsset>("fishdata");
XmlSerializer serializer = new XmlSerializer(typeof(List<FishData>), new XmlRootAttribute("FishContainer"));
StringReader reader = new StringReader(_xml.text);
fishs = (List<FishData>)serializer.Deserialize(reader);
reader.Close();
Debug.Log(fishs.Count);
}
public static FishData GetFishAttributeByName(string name) {
foreach(FishData f in fishs) {
if(f.Name.Equals(name))
return f;
}
return null; //throw
}
}
Fish Data
using System.Xml;
using System.Xml.Serialization;
public class FishData {
[XmlAttribute("Name")] public string Name;
[XmlElement("WaterType")] public string WaterType;
[XmlElement("Price")] public int Price;
[XmlElement("Size")] public int Size;
[XmlElement("Aggression")] public int Aggression;
public override string ToString() {
return Name + " " + WaterType + " " + Price + " " + Size + " " + Aggression;
}
}
The problem is you're deserialising the type List<FishData>, but that's not what your XML file contains at the top level, it's a FishContainer, which in turn has a property of List<FishData> inside it.
Also, you can't deserialise to a static property or class, so you need to remove the static modifiers in FishContainer for the class and property at least. Here is how I would correct FishContainer:
public class FishContainer
{
[XmlArray("Fishies"), XmlArrayItem("Fish")]
public List<FishData> Fishes { get; set; }
public static FishContainer Load(string xml)
{
XmlSerializer serializer = new XmlSerializer(typeof(FishContainer), new XmlRootAttribute("FishContainer"));
using (StringReader reader = new StringReader(xml))
return (FishContainer)serializer.Deserialize(reader);
}
public FishData GetFishAttributeByName(string name)
{
return Fishes
.SingleOrDefault(fish => fish.Name.Equals(name));
}
}
As a rule, personally I try only to use static methods or properties if they don't change or hold the state of something. You can call this as follows;
var fishContainer = FishContainer.Load(_xml);
Console.WriteLine("I have {0} fishes", fishContainer.Fishes.Count);
EDIT: however, as you want it to be statically available with only a single instance of FishContainer, you could do something like this:
public class FishContainer
{
private static FishContainer _instance;
[XmlArray("Fishies"), XmlArrayItem("Fish")]
public List<FishData> Fishes { get; set; }
public static void Load(string xml)
{
XmlSerializer serializer = new XmlSerializer(typeof(FishContainer), new XmlRootAttribute("FishContainer"));
using (StringReader reader = new StringReader(xml))
_instance = (FishContainer)serializer.Deserialize(reader);
}
public static FishData GetFishAttributeByName(string name)
{
if (_instance == null)
throw new InvalidOperationException("FishContainer has not been loaded");
return
_instance
.Fishes
.SingleOrDefault(fish => fish.Name.Equals(name));
}
}
.. and call like this:
FishContainer.Load(_xml);
var someFish = FishContainer.GetFishAttributeByName("test");

Float XML Namespaces to Root?

I have XML within a C# program that looks something like this:
<el1 xmlns="http://URI1">
<el2 xmlns:namespace2="http://URI2"></el2>
<el3>
<el4 xmlns:namespace3="http://URI3"></el4>
</el3>
</el1>
For cleanliness, I would like to move all the namespace declarations to the root element. I cannot change the export that produces this XML, so a solution needs to work on the sample as shown above. What is a good way to accomplish this?
This example is stripped down for brevity, but assume that there are further child elements that actually use these prefixes. These are irrelevant for this question as all the namespace prefix declarations are unique and my goal is only to move them higher in the tree.
I've reviewed the MSDN documentation for XML but there doesn't seem to be a simple way to manipulate namespaces like this. One of the solutions I've tried is interacting with the XML as an XElement and collecting the namespaces based on XAttribute.IsNamespaceDeclaration, replacing each element with its local name and finally creating a new root element with the collected list of namespace XAttributes. That line of experimentation caused a bunch of errors about redefining prefixes, though, and I'm not sure if I'm moving in the right direction or not.
You need to add a prefix
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
using System.Xml;
using System.Xml.Serialization;
namespace ConsoleApplication2
{
class Program
{
const string FILENAME = #"c:\temp\test.xml";
static void Main(string[] args)
{
El1 el1 = new El1()
{
el2 = new El2()
{
el3 = new El3() {
el4 = new El4() {
}
}
}
};
XmlSerializerNamespaces ns = new XmlSerializerNamespaces();
ns.Add("u4", "http://URI4");
ns.Add("u3", "http://URI3");
ns.Add("u2", "http://URI2");
ns.Add("", "http://URI1");
XmlSerializer serializer = new XmlSerializer(typeof(El1));
StreamWriter writer = new StreamWriter(FILENAME);
serializer.Serialize(writer, el1, ns);
writer.Flush();
writer.Close();
writer.Dispose();
}
}
[XmlRoot("el1", Namespace = "http://URI1")]
public class El1
{
[XmlElement("el2", Namespace = "http://URI2")]
public El2 el2 { get; set; }
}
[XmlRoot("el2")]
public class El2
{
[XmlElement("el3", Namespace = "http://URI3")]
public El3 el3 { get; set; }
}
[XmlRoot("el3", Namespace = "http://URI3")]
public class El3
{
[XmlElement("el4", Namespace = "http://URI4")]
public El4 el4 { get; set; }
}
[XmlRoot("el4", Namespace = "http://URI1")]
public class El4
{
}
}
​
You can locate all the xmls attributes using the namespace xpath axis. Add those attributes to the root element. Finally write out the xml using NamespaceHandling.OmitDuplicates, which will leave the namespace declarations on the root and remove them from all the other elements.
var xml = new XmlDocument();
xml.Load("XMLFile1.xml");
// Find all xmlns: attributes
var attributes = xml.DocumentElement.SelectNodes("//namespace::*");
// Add xmlns: attributes to the root
foreach (XmlAttribute attribute in attributes)
xml.DocumentElement.SetAttribute(attribute.Name, attribute.Value);
// Write out results, ignoring duplicate xmlns: attributes
var settings = new XmlWriterSettings();
settings.NamespaceHandling = NamespaceHandling.OmitDuplicates;
settings.Indent = true;
using (var writer = XmlWriter.Create("XMLFile2.xml", settings))
{
xml.Save(writer);
}

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>

Categories