custom(?) serialization of list c# - c#

I'm trying to convert a c# object containing a list into XML. The list comes out fine as:
<image>dummy</image>
<image>dummy2</image>
<image>dummy3</image>
and so forth. I want the tags to be unique like:
<image_1>dummy</image_1>
<image_2>dummy2</image_2>
<image_3>dummy3</image_3>
But i'm not quite sure how to get to that result.
Any input is appreciated :)

A rough sketch of a solution:
Stream stream = ... // your output stream
XmlWriter xml = new XmlWriter(stream)
xml.WriteStartDocument();
int index = 0;
foreach (var x in list) {
xml.WriteStringElement("index_" + index++, x.ToString());
}
xml.Close();
https://learn.microsoft.com/en-us/dotnet/api/system.xml.xmlwriter?view=netcore-3.1

Related

How to parse JSON with Newtonsoft?

I created an ASP.NET Application, where I have to parse a csv file with a json structure.
The csv file itself is structured like:
{"Instance":"abc","Date":"2019-06-03T00:00:02.056Z","Identification":"someFunction","Type":"DurationInMs","Value":"5","iserror":"False""}
I get the jsonCsvData as a string and tried to parse it. Then I want to save some of the elements of this json object into a db.
public IActionResult ReadJsonCsvData(string jsonCsvData)
{
Console.WriteLine("ReadJsonCsvData");
ChartData chartData = new ChartData();
var lines = jsonCsvData.Split("\n");
foreach (var line in lines)
{
var values = JObject.Parse(line);
var first = string.Concat(values["Instance"]); //Output for first: ""
}
}
The problem now is, that the variable first is an empty string. The result should be (like in the json structure example above) "abc".
Thank you in advance!
I don't know if it will help but here is my solution (remove one of " at the end of your Json).
I use the "Jobject" to parse Json as I want. Import this two reference.
using Newtonsoft.Json.Linq;
using Newtonsoft;
Then you have to create your JObject :
JObject o = JObject.Parse(myJsonString);
Then to retrieve specifics data, you just have to search in your object just like you do with a dictionary with key :
instanceFromJson = o["Instance"].ToString;
dateFromJson = o["Date"].ToString;
If you have a table in your "instance" json object you can retrieve all data from this list just like that :
foreach (var item in o["Instance"]["tabFromInstanceObject"])
{
MyList.Add(item);
}

Parsing RDF - dotnetrdf c#

This is my first time ever looking at RDF, and after trying, I've no idea how to parse it. I'm looking at some RDF (in Turtle format) used in the AFF4 file system, here's a portion of it:
<aff4://0295fab8-94b7-4435-bdb3-932cf48e40bd>
a aff4:ImageStream ;
aff4:chunkSize "32768"^^xsd:int ;
aff4:chunksInSegment "2048"^^xsd:int ;
aff4:compressionMethod <http://code.google.com/p/snappy/> ;
aff4:imageStreamHash "82798a275176aa141a2993ca8931535b1303545a0954473f5c5e55b4d8d5a8e3ebdb9e9323e5ecfaf65f8d379a8e2b9150750f5cf44851cf4edb6a2e05372f42"^^aff4:SHA512 ;
aff4:imageStreamIndexHash "039eb2da046cfb8c8d40e6f9b42aae501fb36f9b09b5f29d660d3637f87c37c98c3ee3b995265adff1d2b971fa795317333bf50200e72fdfe9fa96acdb88b6d0"^^aff4:SHA512 ;
aff4:size "185335808"^^xsd:long ;
aff4:stored : ;
aff4:target <aff4://92015053-5f7b-4e5a-a1e7-901d8943cf1f> ;
aff4:version "1"^^xsd:int .
There's a lot of this stuff in the file, but I've no idea how to access any of it, thus far I've cobbled together:
private static void ParseInformationStream(Stream informationStream)
{
Console.WriteLine("Parsing information.turtle file: ");
informationStream.Position = 0;
TurtleParser turtleParser = new TurtleParser();
Graph graph = new Graph();
turtleParser.Load(graph, new StreamReader(informationStream));
foreach (var triple in graph.Triples)
{
Console.WriteLine(triple.Subject);
}
}
This prints out some of the data, but if for example, I wanted to access the aff4:compressionMethod (node?) specifically, how would I go about doing that? I've been reading about Sparql, but it all seems a bit overkill for what I need.
Any input or advice would be appreciated.
You can use the methods of the IGraph interface to access the contents of the parsed graph. For example the following will retrieve all image streams (in Turtle "a" is a shortcut for the rdf:type predicate) and print out the compression method for each stream:
// Get the node for rdf:type
var rdfType = graph.CreateUriNode(new Uri(RdfSpecsHelper.RdfType));
// Get the node for the aff4:ImageStream type
var imageStream = graph.GetUriNode("aff4:ImageStream");
// Get the node for the aff4:compressionMethod predicate
var compressionMethod = graph.CreateUriNode("aff4:compressionMethod");
// Get the streams (the subject of x a aff4:ImageStream in the Turtle)
var imageStreams = graph.GetTriplesWithPredicateObject(rdfType, imageStream).Select(t => t.Subject);
foreach (var streamInstance in imageStreams)
{
// Get the first compressionMethod value for the stream instance
var compression = graph.GetTriplesWithSubjectPredicate(streamInstance, compressionMethod)
.Select(t => t.Object).FirstOrDefault();
Console.WriteLine("Stream " + streamInstance + " uses compression method " + compression);
}
For more on accessing nodes an triples in a graph in dotNetRDF, please see https://github.com/dotnetrdf/dotnetrdf/wiki/UserGuide-Working-With-Graphs

How to extract specific data from XML data

I am using the following code snippet to parse and convert some XML data to CSV. I can convert the entire XML data and dump it into a file, however my requirements have changed and now I'm confused.
public void xmlToCSVfiltered(string p, int e)
{
string all_lines1 = File.ReadAllText(p);
all_lines1 = "<Root>" + all_lines1 + "</Root>";
XmlDocument doc_all = new XmlDocument();
doc_all.LoadXml(all_lines1);
StreamWriter write_all = new StreamWriter(FILENAME2);
XmlNodeList rows_all = doc_all.GetElementsByTagName("XML");
List<string[]> filtered = new List<string[]>();
foreach (XmlNode rowtemp in rows_all)
{
List<string> children_all = new List<string>();
foreach (XmlNode childtemp in rowtemp.ChildNodes)
{
children_all.Add(Regex.Replace(childtemp.InnerText, "\\s+", " ")); // <------- Fixed the Bug , Advisories dont span
}
string.Join(",", children_all.ToArray());
//write_all.WriteLine(string.Join(",", children_all.ToArray()));
if (children_all.Contains(e.toString()))
{
filtered.Add(children_all.ToArray());
write_all.WriteLine(children_all);
}
}
write_all.Flush();
write_all.Close();
foreach (var res in filtered)
{
Console.WriteLine(string.Join(",", res));
}
}
My input looks something like the following... My objective now is to only convert those "events" and compile into a CSV which have a certain number. Lets say, for example, I only want to convert to CSV those events who's 2nd data value under element <EVENT> is 4627. It would only convert those events and in the case of the input below, both mentioned below.
<XML><HEADER>1.0,770162,20121009133435,3,</HEADER>20121009133435,721,5,1,0,0,0,00:00,00:00,<EVENT>00032134826064957,4627,</EVENT><DRUG>1,1872161156,7,0,10000</DRUG><DOSE>1,0,5000000,0,10000000,0</DOSE><CAREAREA>1 </CAREAREA><ENCOUNTER></ENCOUNTER><ADVISORY>Keep it simple or spell
tham ALL out. For some reason
that is not the case
please press the on button
when trying to activate
device codes also available on
list</ADVISORY><CAREGIVER></CAREGIVER><PATIENT></PATIENT><LOCATION>20121009133435,00-1d-71-0a-71-80,-66</LOCATION><ROUTE></ROUTE><SITE></SITE><POWER>0,50</POWER></XML>
<XML><HEADER>2.0,773162,20121009133435,3,</HEADER>20121004133435,761,5,1,0,0,0,00:00,00:00,<EVENT>00032134826064957,4627,</EVENT><DRUG>1,18735166156,7,0,10000</DRUG><DOSE>1,0,5000000,0,10000000,0</DOSE><CAREAREA>1 </CAREAREA><ENCOUNTER></ENCOUNTER><ADVISORY>Keep it simple or spell
tham ALL out. For some reason
that is not the case
please press the on button
when trying to activate
device codes also available on
list</ADVISORY><CAREGIVER></CAREGIVER><PATIENT></PATIENT><LOCATION>20121009133435,00-1d-71-0a-71-80,-66</LOCATION><ROUTE></ROUTE><SITE></SITE><POWER>0,50</POWER></XML>
.. goes on
What my approach has been so far is to convert everything to CSV and store it in some sort of data structure and then query that data structure line by line and look if that number exists and if yes, write it to a file line by line. My function takes the path of the XML file and the number we are looking for in the XML data as parameters. I'm new to C# and I cannot understand how I would go about changing my function above. Any help will be appreciated!
EDIT:
Sample Input:
<XML><HEADER>1.0,770162,20121009133435,3,</HEADER>20121009133435,721,5,1,0,0,0,00:00,00:00,<EVENT>00032134826064957,4627,</EVENT><DRUG>1,1872161156,7,0,10000</DRUG><DOSE>1,0,5000000,0,10000000,0</DOSE><CAREAREA>1 </CAREAREA><ENCOUNTER></ENCOUNTER><ADVISORY>Keep it simple or spell
tham ALL out. For some reason
that is not the case
please press the on button
when trying to activate
device codes also available on
list</ADVISORY><CAREGIVER></CAREGIVER><PATIENT></PATIENT><LOCATION>20121009133435,00-1d-71-0a-
<XML><HEADER>1.0,770162,20121009133435,3,</HEADER>20121009133435,721,5,1,0,0,0,00:00,00:00,<EVENT>00032134826064957,4623,</EVENT><DRUG>1,1872161156,7,0,10000</DRUG><DOSE>1,0,5000000,0,10000000,0</DOSE><CAREAREA>1 </CAREAREA><ENCOUNTER></ENCOUNTER><ADVISORY>Keep it simple or spell
tham ALL out. For some reason
that is not the case
please press the on button
when trying to activate
device codes also available on
list</ADVISORY><CAREGIVER></CAREGIVER><PATIENT></PATIENT><LOCATION>20121009133435,00-1d-71-0a-
Required Output:
1.0,770162,20121009133435,3,,20121009133435,721,5,1,0,0,0,00:00,00:00,,00032134 26064957,4627,1,,1872161156,7,0,10000,1,0,5000000,0,10000000,0,1 ,,Keep it simple or spell
tham ALL out. For some reason
that is not the case
please press the on button
when trying to activate
device codes also available on
list,,,20121009133435,00-1d-71-0a-71-80,-66,,,0,50
The above will be the case if I call xmlToCSVfiltered(file, 4627);
Also note that, the output will be a single horizontal line as in CSV files but I can't really format it here for it to look like that.
I changed XmlDocumnet to XDocument so I can use Xml Linq. I also for testing used a StringReader to read the string instead of reading from a file. You can convert code back to your original File.ReadAlltext.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml;
using System.Xml.Linq;
using System.IO;
using System.Text.RegularExpressions;
namespace ConsoleApplication1
{
class Program
{
const string FILENAME2 = #"c:\temp\test.txt";
static void Main(string[] args)
{
string input =
"<XML><HEADER>1.0,770162,20121009133435,3,</HEADER>20121009133435,721,5,1,0,0,0,00:00,00:00,<EVENT>00032134826064957,4627,</EVENT><DRUG>1,1872161156,7,0,10000</DRUG><DOSE>1,0,5000000,0,10000000,0</DOSE><CAREAREA>1 </CAREAREA><ENCOUNTER></ENCOUNTER><ADVISORY>Keep it simple or spell\n" +
"tham ALL out. For some reason \n" +
"that is not the case\n" +
"please press the on button\n" +
"when trying to activate\n" +
"device codes also available on\n" +
"list</ADVISORY><CAREGIVER></CAREGIVER><PATIENT></PATIENT><LOCATION>20121009133435,00-1d-71-0a-71-80,-66</LOCATION><ROUTE></ROUTE><SITE></SITE><POWER>0,50</POWER></XML>\n" +
"<XML><HEADER>2.0,773162,20121009133435,3,</HEADER>20121004133435,761,5,1,0,0,0,00:00,00:00,<EVENT>00032134826064957,4627,</EVENT><DRUG>1,18735166156,7,0,10000</DRUG><DOSE>1,0,5000000,0,10000000,0</DOSE><CAREAREA>1 </CAREAREA><ENCOUNTER></ENCOUNTER><ADVISORY>Keep it simple or spell\n" +
"tham ALL out. For some reason\n" +
"that is not the case\n" +
"please press the on button\n" +
"when trying to activate\n" +
"device codes also available on\n" +
"list</ADVISORY><CAREGIVER></CAREGIVER><PATIENT></PATIENT><LOCATION>20121009133435,00-1d-71-0a-71-80,-66</LOCATION><ROUTE></ROUTE><SITE></SITE><POWER>0,50</POWER></XML>\n";
xmlToCSVfiltered(input, 4627);
}
static public void xmlToCSVfiltered(string p, int e)
{
//string all_lines1 = File.ReadAllText(p);
StringReader reader = new StringReader(p);
string all_lines1 = reader.ReadToEnd();
all_lines1 = "<Root>" + all_lines1 + "</Root>";
XDocument doc_all = XDocument.Parse(all_lines1);
StreamWriter write_all = new StreamWriter(FILENAME2);
List<XElement> rows_all = doc_all.Descendants("XML").Where(x => x.Element("EVENT").Value.Split(new char[] {','}).Skip(1).Take(1).FirstOrDefault() == e.ToString()).ToList();
List<string[]> filtered = new List<string[]>();
foreach (XElement rowtemp in rows_all)
{
List<string> children_all = new List<string>();
foreach (XElement childtemp in rowtemp.Elements())
{
children_all.Add(Regex.Replace(childtemp.Value, "\\s+", " ")); // <------- Fixed the Bug , Advisories dont span
}
string.Join(",", children_all.ToArray());
//write_all.WriteLine(string.Join(",", children_all.ToArray()));
if (children_all.Contains(e.ToString()))
{
filtered.Add(children_all.ToArray());
write_all.WriteLine(children_all);
}
}
write_all.Flush();
write_all.Close();
foreach (var res in filtered)
{
Console.WriteLine(string.Join(",", res));
}
}
}
}
​
I have made some assumptions since it was not clear to me from the question
Assumptions
1. I am assuming you know that you need to check node event and you need to second position element from there.
2. You know the delimiter between the values in node. for eg. ',' here in events
public void xmlToCSVfiltered(string p, int e, string nodeName, char delimiter)
{
//get the xml node
XDocument xml = XDocument.Load(p);
//get the required node. I am assuming you would know. For eg. Event Node
var requiredNode = xml.Descendants(nodeName);
foreach (var node in requiredNode)
{
if (node == null)
continue;
//Also here, I am assuming you have the delimiter knowledge.
var valueSplit = node.Value.Split(delimiter);
foreach (var value in valueSplit)
{
if (value == e.ToString())
{
AddToCSV();
}
}
}
}

Select specific nodes in XML with LINQ

I'm writing a function that loads and XML document and converts it to a CSV. Since I need only some values from the XML file, the goal i'm trying to achieve is to select only the nodes I'm interested in.
Here's my code:
XDocument csvDocument = XDocument.Load(tempOutput);
StringBuilder csvBuilder = new StringBuilder(1000);
foreach (XElement node in csvDocument.Descendants("Sample"))
{
foreach (XElement innerNode in node.Elements())
{
csvBuilder.AppendFormat("{0},", innerNode.Value);
}
csvBuilder.Remove(csvBuilder.Length -1, 1);
csvBuilder.AppendLine();
}
csvOut = csvBuilder.ToString();
But, in this way I'm selectin ALL the child nodes inside the "Sample" node.
In the XML, "Sample" tree is:
<Sample Type="Object" Class ="Sample">
<ID>1</ID>
<Name>10096</Name>
<Type>2</Type>
<Rep>0</Rep>
<Selected>True</Selected>
<Position>1</Position>
<Pattern>0</Pattern>
</Sample>
Code works flawlessly, but I need only "ID" and "Selected" to be selected and their values written inside the CSV file.
Could anyone point me in the right direction, please?
Thanks.
Learn more about Linq-to-xml here. You're not really taking advantage of the 'linq-edness' of XObjects
var samples = csvDocument.Descendants("Sample")
.Select(el => new {
Id = el.Element("ID").Value,
Selected = el.Elemnt("Selected").Value
});
This creates for you an IEnumerable<T> where 'T' is an anonymous type with the properties Id and Selected.
You can parse (int.Parse or bool.Parse) the Id and Selected values for type safety. But since you are simply writing to a StringBuilder object you may not care ...just an FYI.
The StringBuilder object can then be written as follows:
foreach (var sample in samples) {
csvBuilder.AppendFormat(myFormattedString, sample.Id, sample.Selected);
}
The caveat to this is that your anonymous object and the for-each loop should be within the same scope. But there are ways around that if necessary.
As always, there is more than one way to skin a cat.
Update ...in ref. to comment:
foreach (XElement node in csvDocument.Descendants("Sample"))
{
foreach (XElement innerNode in node.Elements())
{
// this logic assumes different formatting for values
// otherwise, change if statement to || each comparison
if(innerNode.Name == "ID") {
// append/format stringBuilder
continue;
}
if(innerNode.Name == "Selected") {
// append/format stringBuilder
continue;
}
}
csvBuilder.Remove(csvBuilder.Length -1, 1);
csvBuilder.AppendLine();
}

How do I pass a collection of strings as a TextReader?

I am using the CSVHelper library, which can extract a list of objects from a CSV file with just three lines of code:
var streamReader = // Create a reader to your CSV file.
var csvReader = new CsvReader( streamReader );
List<MyCustomType> myData = csvReader.GetRecords<MyCustomType>();
However, by file has nonsense lines and I need to skip the first ten lines in the file. I thought it would be nice to use LINQ to ensure 'clean' data, and then pass that data to CsvFReader, like so:
public TextReader GetTextReader(IEnumerable<string> lines)
{
// Some magic here. Don't want to return null;
return TextReader.Null;
}
public IEnumerable<T> ExtractObjectList<T>(string filePath) where T : class
{
var csvLines = File.ReadLines(filePath)
.Skip(10)
.Where(l => !l.StartsWith(",,,"));
var textReader = GetTextReader(csvLines);
var csvReader = new CsvReader(textReader);
csvReader.Configuration.ClassMapping<EventMap, Event>();
return csvReader.GetRecords<T>();
}
But I'm really stuck into pushing a 'static' collection of strings through a stream like a TextReaer.
My alternative here is to process the CSV file line by line through CsvReader and examine each line before extracting an object, but I find that somewhat clumsy.
The StringReader Class provides a TextReader that wraps a String. You could simply join the lines and wrap them in a StringReader:
public TextReader GetTextReader(IEnumerable<string> lines)
{
return new StringReader(string.Join("\r\n", lines));
}
An easier way would be to use CsvHelper to skip the lines.
// Skip rows.
csvReader.Configuration.IgnoreBlankLines = false;
csvReader.Configuration.IgnoreQuotes = true;
for (var i = 0; i < 10; i++)
{
csvReader.Read();
}
csvReader.Configuration.IgnoreBlankLines = false;
csvReader.Configuration.IgnoreQuotes = false;
// Carry on as normal.
var myData = csvReader.GetRecords<MyCustomType>;
IgnoreBlankLines is turned off in case any of those first 10 rows are blank. IgnoreQuotes is turned off so you don't get any BadDataExceptions if those rows contain a ". You can turn them back on after for normal functionality again.
If you don't know the amount of rows and need to test based on row data, you can just test csvReader.Context.Record and see if you need to stop. In this case, you would probably need to manually call csvReader.ReadHeader() before calling csvReader.GetRecords<MyCustomType>().

Categories

Resources