Editing XML Output With LINQ - c#

I have an XML output file from a process being run that needs the contents of various fields edited according to a collection of tables in our database. For example, what's included in
<?xml version="1.0" encoding="utf-8"?>
<ArrayOfUserReportPreviewListDto xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<UserReportPreviewListDto>
<ExtensionData />
<Id>previewReportFieldsId</Id>
<Field0>-7</Field0>
<Field1>L</Field1>
<Field2>Lab Work Complete</Field2>
<Field3>False</Field3>
<Field4>LabWorkComplete</Field4>
<Field6>False</Field6>
</UserReportPreviewListDto>
<UserReportPreviewListDto>
<ExtensionData />
<Id>previewReportFieldsId</Id>
<Field0>-6</Field0>
<Field1>S</Field1>
<Field2>Sent to Lab</Field2>
<Field3>False</Field3>
<Field4>SentToLab</Field4>
<Field6>False</Field6>
</UserReportPreviewListDto>
<UserReportPreviewListDto>
<ExtensionData />
<Id>previewReportFieldsId</Id>
<Field0>-5</Field0>
<Field1>V</Field1>
<Field2>Void</Field2>
<Field3>False</Field3>
<Field4>Void</Field4>
<Field6>True</Field6>
<Field7>12/11/2013</Field7>
<Field9>769</Field9>
</UserReportPreviewListDto>
would need Field4 changed from LabWorkComplete (tblEnum.FieldTypeDesc) to 2 (tblEnum.FieldTypeNum).
I'm very new to using LINQ, and am not even completely sure it's the best route for this. I've created a DataSet in the project, with a DataTable populated from the database with what I need to work with. And...that's as far as I've got. Right now I'm using a massive list of tedious If statements to accomplish this, and am thinking this avenue may be more efficient than a collection of statements like this.
var xe = XElement.Load("serializer.xml");
string field4Value = xe.XPathSelectElement(#"/UserReportPreviewListDto/Field4").Value;
if (field4Value == "Incomplete")
{
xe.XPathSelectElement(#"/UserReportPreviewListDto/Field4").Value = "0";
}
else if (field4Value == "SendToLab")
{
xe.XPathSelectElement(#"/UserReportPreviewListDto/Field4").Value = "1";
}
else if (field4Value == "LabWorkComplete")
{
xe.XPathSelectElement(#"/UserReportPreviewListDto/Field4").Value = "2";
}
So that's where I am. If LINQ wouldn't be the best avenue, what would be? If it would be, what would be the best way to do it? Additionally, any particularly helpful resources along these lines that can be recommended would be appreciated; I'd much rather learn code than copy code. I'd hate to have to ask this again next week, after all.

Your XML structure is weird. Field0...Field6 is not common, there are usually meaningful names in there. You can always write a function that encapsulates string to integer string conversion, and just provide an xpath as an argument. Then go higher level, provide xpath + conversion delegate, and from this point it's as easy as one line per property. Here is an implementation example:
using System;
using System.Xml.Linq;
using System.Xml.XPath;
namespace ConsoleApplication1
{
class Program
{
static void Main(string[] args)
{
var xe = XElement.Load("serializer.xml");
ConvertValue(xe, #"/UserReportPreviewListDto/Field4", TranslateValueField4);
}
private static void ConvertValue(XElement xe, string xpath, TranslateValue translator)
{
string field4Value = xe.XPathSelectElement(xpath).Value;
xe.XPathSelectElement(xpath).Value = translator(field4Value);
}
private delegate string TranslateValue(string value);
private static string TranslateValueField4(string value)
{
switch (value)
{
case "Incomplete" :
return "0";
case "SendToLab" :
return "1";
case "LabWorkComplete":
return "2";
default:
throw new NotImplementedException(); //or provide handling for unknown values
}
}
}
}
You can also avoid using xpath, and just iterate using foreach:
static void Main(string[] args)
{
var doc = XDocument.Load(#"input.xml");
foreach (var xe in doc.Root.Elements("UserReportPreviewListDto"))
{
ConvertValue(xe, "Field4", TranslateValueField4);
}
//doc.Save(path);
}
private static void ConvertValue(XElement xe, string fieldName, TranslateValue translator)
{
//.Element returns Nothing if element is missing, may want to handle this case
XElement field4 = xe.Element(fieldName);
string field4Converted = TranslateValueField4(field4.Value);
field4.SetValue(field4Converted);
}

I always, always, always prefect to store xml into custom classes and then work with them inside my C# environment. Makes the process of modifying it feel much more natural. Please look at my question here to see the best way to do this. It takes a bit more time, but it makes things SO much easier in the long run. You said you wanted the best route and to learn, right? ;)

Related

Parsing Json information from a Json file as a string

I am looking for some help with regards to Parsing the the value "mppdemo" in the below json file (See screenshot)
{
"client":{
"isWebLogin":false,
"registryName": "mpdemo",
"walletCode": "Local"
}
}
I have done some research in and arround the webs but alot of the examples wither are out dated or dont work.
This is what i have tried
//JObject T = JObject.Parse(File.ReadAllText(DownloadConfigFilelocation));
var source = File.ReadAllText(DownloadConfigFilelocation);
var JavaScriptSerializer MySerializer = new JavaScriptSerializer();
var myObj = MySerializer.Deserialize<T>(source);
var RegistryName = myObj.searchResults[0].hotelID;
MessageBox.Show(RegistryName);
The above doesnt pick up the JavaScriptSerializer function from the library even though im using the using System.Web.Script.Serialization;
Can someone help me get this code segment to work
I hope i have provided enough info
EDIT: I just realized that you're having another problem - that your compiler does not recognize the System.Web.Script.Serialization.JavaScriptSerializer type. You'll need to add a reference to System.Web.Extensions.dll to your project. I don't know what IDE you are using, but for example in SharpDevelop you can right click References > Add Reference > in filter start typing "System.Web.Extensions" > among results find "System.Web.Extensions" and double click it (it will be moved to lower window) > hit OK and compile your project.
If you still want to use System.Web.Script.Serialization.JavaScriptSerializer, I'd probably do it like this:
using System;
using System.Text.RegularExpressions;
using System.Web.Script.Serialization;
namespace jsonhratky
{
public static class Program {
public static void Main(string[] args)
{
var instance = new JsonParsingTest();
}
}
public class JsonParsingTest
{
class Response {
public Client client;
}
class Client {
public bool isWebLogin;
public string registryName;
public string walletCode;
}
const string JSON_EXAMPLE = ("{" + ("\"client\":{" + ("\"isWebLogin\":false," + ("\"registryName\": \"mpdemo\"," + ("\"walletCode\": \"Local\"" + ("}" + "}"))))));
public JsonParsingTest() {
// Solution #1 using JavaScriptSerializer
var serializer = new JavaScriptSerializer();
Response parsed = serializer.Deserialize<Response>(JSON_EXAMPLE);
Console.WriteLine("parsed isWebLogin: " + parsed.client.isWebLogin);
Console.WriteLine("parsed registryName: " + parsed.client.registryName);
Console.WriteLine("parsed walletCode: " + parsed.client.walletCode);
// Solution #2 (not recommended)
var matches = Regex.Match(JSON_EXAMPLE, "registryName\":.*?\"([^\"]+)\"", RegexOptions.Multiline);
if (matches.Success) {
Console.WriteLine("registryName parsed using Regex: " + matches.Groups[1].Value);
} else {
Console.WriteLine("Solution using Regex failed.");
}
Console.WriteLine("Press any key to continue...");
Console.ReadKey();
}
}
}
You need to create a "POJO" class (there's probably another term in C# for plain old classes) with fields matching those in your string response. Since your fields isWebLogin, registryName and walletCode are not directly part of main object (Response) but they belong to sub-class (Client), you need two classes: Response (or call it whatever you want) and then the field "client" must match string in response (as well as the fields of the sub-class).
Result:
Anyway, I also included a solution using Regex, but I absolutely don't recommend that. It's suitable only as a workaround and only then if you know that your response will never contain more than one "client" objects.
The problem seems to be in this line of your code var myObj = MySerializer.Deserialize<T>(source); You need to give the type of object instead of T.

How can I retrieve a generated value from my C#-code into my web.config file?

I know, that the following code looks naive, but it should only bring to mind, what I want to achieve.
My web.config file:
<connectionStrings>
<add name="ADConnectionString" connectionString="AppData.GetConnectionString()" />
</connectionStrings>
I want to get the string from C#:
public class AppData
{
public static string GetConnectionString()
{
return "LDAP://expample.domain.com:389/DC=example,DC=domain,DC=com";
}
}
I know it is possible to get data from the web.config in the C# code (The AppSettings for example). But is the opposite also possible?
I dont think it is possible as it is just an XML file. All you can do in XML file is add comment,create nodes,add attributes or add nested elements etc etc but you cant add any code to it.
However I think there are certain languages that allow you to do so
https://stackoverflow.com/questions/24486772/write-php-code-inside-xml-file
I DO NOT recommend use it. But you actually can do something like this:
using System.Web.Configuration;
using System.Configuration;
var config = WebConfigurationManager.OpenWebConfiguration(null);
var connectionStringsSection = (ConnectionStringsSection)config.GetSection("connectionStrings");
connectionStringsSection.ConnectionStrings["ADConnectionString"].ConnectionString = AppData.GetConnectionString();
config.Save();
Use Reflection
Here's an example:
private string test = WebConfigurationManager.ConnectionStrings["Test"].ConnectionString; //returns "ExecuteTest" -- note! no parenthesis!
protected void Page_Load(object sender, EventArgs e)
{
MethodInfo m = this.GetType().GetMethod(test); //expects static method
if (m != null)
{
object result = m.Invoke(this, new object[] { });
}
}
private static void ExecuteTest()
{
//do stuff
}
But no, you can't edit/pull data into the web.config on the fly. Changing the web.config will cause the application to restart killing all sessions. It's possible to build the web.config prior to starting but once it's up and running it's essentially locked down.

How to get a string name to resolve to an object (i.e. in the sense of what it is)

For example I can do something like:
switch (myString)
case "rectangle":
o = new rect();
break;
case "ellipse"
etc...
but how do I not do the above, i.e. just have one line of code that gets the object directly from the string. Imagine, for example, a button and whatever it says when the user clicks it, it takes the displayed text and creates an object from it.
If the name is the exact same thing as the string, you can do something like this:
using System;
using System.Reflection;
class Example
{
static void Main()
{
var assemblyName = Assembly.GetExecutingAssembly().FullName;
var o = Activator.CreateInstance(assemblyName, "Example").Unwrap();
}
}
A simpler approach would look like this:
using System;
using System.Reflection;
class Example
{
static void Main()
{
var type = Assembly.GetExecutingAssembly().GetType("Example");
var o = Activator.CreateInstance(type);
}
}
But keep in mind that this is a very simple example that doesn't involve namespaces, strong-named assemblies, or any other complicated things that crop up in bigger projects.
Check Activator.CreateInstace(Type)
or Activator.CreateInstance(string, string)
He descf,
Have you tried Activator.CreateInstace("assemblyname", "typename");
A Factory pattern would decouple the code from the string. Please take a look at this dofactory page for both the UML and a C# example of the Factory pattern.

Xml Attribute not showing up on ListBox - C#

I'm getting a null reference exception whenever it tries to add the packages titles info and other attributes but the attributes exist and the proper package is selected
Heres the code:
private void categorylist_listview_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
XmlDocument LoadPackageList = new XmlDocument();
//Removes the text "Select A Category" and refrehes the form
packagelist_listbox.Items.Remove(SelectaCategory_listbox);
if (categorylist_listview.SelectedItem == WWW_listviewitem)
{
LoadPackageList.Load("www.xml");
XmlNodeList WWWPackageList = LoadPackageList.SelectNodes("/Packages/*");
int countthenodes = 0;
foreach (XmlNode WWWPackages in WWWPackageList)
{
//Cycles through all the packages and assings them to a string then adds it to the packagelist
countthenodes++;
PackageTitle[countthenodes] = WWWPackages.Attributes["title"].ToString();
PackageInfo[countthenodes] = WWWPackages.Attributes["info"].ToString();
PackageDownloadUrl[countthenodes] = WWWPackages.Attributes["downloadurl"].ToString();
PackageTags[countthenodes] = WWWPackages.Attributes["tags"].ToString();
packagelist_listbox.Items.Add(PackageTitle[countthenodes]);
}
Refresh(packagelist_listbox);
}
}
It Errors out at PackageTitle[countthenodes] = WWWPackages.Attributes["title"].ToString();
XML File:
<Packages>
<Firefox title="Mozilla Firefox" tags="www firefox web browser mozilla" info="http://google.com" downloadurl="http://firefox.com"></Firefox>
</Packages>
The Variables are declared
public string[] PackageTags;
public string[] PackageTitle;
public string[] PackageInfo;
public string[] PackageDownloadUrl;
At the very beginning of the file
Well, the first problem is that calling ToString() on an XmlAttribute isn't going to do what you want. You should use the Value property. However, I don't believe that's causing a NullReferenceException unless the data isn't quite as you showed it. Here's a short but complete program which works fine:
using System;
using System.Xml;
class Test
{
static void Main()
{
XmlDocument doc = new XmlDocument();
doc.Load("test.xml");
XmlNodeList list = doc.SelectNodes("/Packages/*");
foreach (XmlNode node in list)
{
Console.WriteLine(node.Attributes["title"].Value);
}
}
}
That displays "Mozilla Firefox" with the XML you gave us.
Options:
Your real XML actually contains an element without a title attribute
Perhaps PackageTitle is null?
It would help if you could produce a short but complete program demonstrating the problem. Ideally it should avoid using a GUI - I can't see anything here which is likely to be GUI-specific.
If you could tell us more about PackageTitle and how it's being initialized, that would help too. How are you expecting it to just keep expanding for as many elements as you find? Or is it an array which is initialized to a larger size than you ever expect to find elements?

Best way to implement IXmlSerializable ReadXml() using XPath

I'm implementing the ReadXml() method of IXmlSerializable and I figure using XPath is probably the nicest way to do this.
However, ReadXml() needs to handle the reader position properly.
So given that my WriteXml() produces something like this:
<ObjectName>
<SubNode>2</SubNode>
<SubNode>2</SubNode>
</ObjectName>
Is there a better way than (this terrible way) below to ensure the Reader is correctly positioned afterwards?
public override void ReadXml(System.Xml.XmlReader reader)
{
reader.Read(); /* Read Opening tag */
/* Using reader.ReadStartElement("ObjectName") reads forward a node,
i.e. current node becomes the first <SubNode>
whereas Read() doesn't even when the documentation says they both do
*/
XPathNavigator n = MakeXPathNavigator(reader.ReadSubtree());
XPathNodeIterator nodes = n.Select(".//SubNode");
while (nodes.MoveNext())
{
/* Do stuff with nodes */
_values.Add(nodes.Current.ValueAsInt);
}
reader.Skip(); /* Skip reader forward */
}
public static XPathNavigator MakeXPathNavigator(XmlReader reader)
{
try
{
return new XPathDocument(reader).CreateNavigator();
}
catch(XmlException e)
{
throw e; /* Maybe hide/deal with exception */
}
}
I suspect you might run into some performance issues if using that approach routinely. Since your xml is relatively simple, I strongly suspect that you would do better just using XmlReader directly...
...but doing so isn't easy; IMO, it is better to try to avoid the need to implement IXmlSerializable (juts using regular collection properties etc) - it is a common cause of bugs and frustration.

Categories

Resources