I have
an old WCF SOAP service from my server,
an .NET Framework application.
an .NET Framework library.
I want to upgrade my library first to netstandard2.0.
Everything works well, i can regenerate WCF Client files.
However, DataTable have changed to ...TableResult with XmlElement.
So, i know how to change XmlElement to DataTable, but how do I change DataTable to XmlElement?
public static class Transform
{
public static DataTable ToDataTable(XmlElement xmlElement)
{
using var reader = new XmlNodeReader(xmlElement);
var datatable = new DataTable();
datatable.ReadXml(reader);
return datatable;
}
public static XmlElement ToXmlElement(DataTable datatable)
{
throw new NotImplementedException();
}
}
You have to use GroupBy to group the rows, then select the parts you want into XElements.
Here is an example:
var xml = new XElement(table.TableName, table.Rows.Cast<DataRow>()
.GroupBy(row => (string)row[0])
.Select(g =>
new XElement(table.Columns[0].ColumnName,
new XElement("label", g.Key),
g.GroupBy(row => (string)row[1])
.Select(g1 =>
new XElement(table.Columns[1].ColumnName,
new XElement("label", g1.Key),
new XElement(table.Columns[2].ColumnName,
g1.Select(row =>
new XElement("label", (string)row[2])
)
)
)
)
)
)
)
or you can use dataset
DataSet ds = new DataSet();
ds.Tables.Add(table);
XmlDocument XMLDoc = new XmlDocument();
Console.WriteLine(ds.GetXml().ToString());
// In your case:
return XMLDoc.DocumentElement;
You may use ds.Write.xml, this will have a Stream to put the output into. If you need it, try the method below:
public static class Extensions
{
public static string ToXml(this DataSet ds)
{
using (var memoryStream = new MemoryStream())
{
using (TextWriter streamWriter = new StreamWriter(memoryStream))
{
var xmlSerializer = new XmlSerializer(typeof(DataSet));
xmlSerializer.Serialize(streamWriter, ds);
return Encoding.UTF8.GetString(memoryStream.ToArray());
}
}
}
}
USAGE:
var xmlString = ds.ToXml();
Response.Write(ds.ToXml());
And you can check the docs for help.
Related
I have an XML with the below structure:
<Entity Record={ID}>
<GeneralInfo>
Attributes here
</GeneralInfo>
<DetailInfo>
Attributes here
</DetailInfo>
</Entity>
I've managed to generate a simplified version of the XML with the below structure:
<Entity>
Attributes here
</Entity>
However the two things I'm struggling with are:
How to add the record ID to "Entity"
How to add the hierarchies in (not sure the terminology for this in XMLs)
The code I have is:
try
{
DataTable dt = new DataTable{ TableName = "Entity" };
OleDbDataAdapter adapter = new OleDbDataAdapter();
adapter.Fill(dt, Dts.Variables["User::ResultSet"].Value);
MessageBox.Show(dt.Rows.Count.ToString());
System.IO.StringWriter writer = new System.IO.StringWriter();
dt.WriteXml(writer, XmlWriteMode.IgnoreSchema, false);
string xmlOutput = writer.ToString();
File.WriteAllText(output, xmlOutput);
}
catch (Exception e)
{
MessageBox.Show(e.Message.ToString());
}
Check the XElement class: https://learn.microsoft.com/en-us/dotnet/csharp/programming-guide/concepts/linq/creating-xml-trees-linq-to-xml-2
The basic example is this:
XElement contacts =
new XElement("Contacts",
new XElement("Contact",
new XElement("Name", "Patrick Hines"),
new XElement("Phone", "206-555-0144"),
new XElement("Address",
new XElement("Street1", "123 Main St"),
new XElement("City", "Mercer Island"),
new XElement("State", "WA"),
new XElement("Postal", "68042")
)
)
);
Using the ToString() function on the XElement object will return the value in string format.
To generate attributes like the id, you can use the XAttribute class like this:
XElement phone = new XElement("Phone",
new XAttribute("Type", "Home"),
"555-555-5555");
Console.WriteLine(phone);
I want to generate an XML Schema based upon a class, just as you can
do with the Xsd.exe tool.
E.g. xsd.exe /type: typename /outputdir:c:\ assmeblyname.
Is there a way to do this by using classes in the .NET Framework instead of using the standalone tool?
I'm sure I've seen information about task references or similar - i.e. something programmatic - that can be used in place of some of these standalone utilities, or that some standalone utilities get their features through the FCL or a Microsoft API.
Found this which looks like it should do the trick...
public static string GetSchema<T>()
{
XmlAttributeOverrides xao = new XmlAttributeOverrides();
AttachXmlAttributes(xao, typeof(T));
XmlReflectionImporter importer = new XmlReflectionImporter(xao);
XmlSchemas schemas = new XmlSchemas();
XmlSchemaExporter exporter = new XmlSchemaExporter(schemas);
XmlTypeMapping map = importer.ImportTypeMapping(typeof(T));
exporter.ExportTypeMapping(map);
using (MemoryStream ms = new MemoryStream())
{
schemas[0].Write(ms);
ms.Position = 0;
return new StreamReader(ms).ReadToEnd();
}
}
do this:
public string GetFullSchema() {
string #namespace = "yourNamespace";
var q = from t in Assembly.GetExecutingAssembly().GetTypes()
where t.IsClass && t.Namespace == #namespace
select t;
XmlReflectionImporter importer = new XmlReflectionImporter(#namespace);
XmlSchemas schemas = new XmlSchemas();
XmlSchemaExporter exporter = new XmlSchemaExporter(schemas);
foreach (var x in q)
{
var map = importer.ImportTypeMapping(x);
exporter.ExportTypeMapping(map);
}
using (MemoryStream ms = new MemoryStream())
{
schemas[0].Write(ms);
ms.Position = 0;
return new StreamReader(ms).ReadToEnd();
}
}
My project requires a functionality to convert the input XML file into DataTable.
I am using the following code to do that.
DataSet ds = new DataSet();
ds.Locale = CultureInfo.InvariantCulture;
dataSourceFileStream.Seek(0, SeekOrigin.Begin);
ds.ReadXml(dataSourceFileStream);
dt = ds.Tables[0];
This works quiet right unless the input XML has duplicate elements, for eg, if the XML file is like below:
<?xml version="1.0" encoding="iso-8859-1"?>
<DocumentElement>
<data>
<DATE>27 September 2013</DATE>
<SCHEME>Test Scheme Name</SCHEME>
<NAME>Mr John</NAME>
<SCHEME>Test Scheme Name</SCHEME>
<TYPE>1</TYPE>
</data>
</DocumentElement>
As you can see above, the element SCHEME appears twice. when this kind of XML file comes ds.ReadXml(dataSourceFileStream); fails to return right data table.
Any better way to handle this?
Looks like you have to fix the XML first. You can do this by using the XDocument and associated classes. But first you need to create a EqualityComparer which compares two XElements based on their name:
public class MyEqualityComparer : IEqualityComparer<XElement>
{
public bool Equals(XElement x, XElement y)
{
return x.Name == y.Name;
}
public int GetHashCode(XElement obj)
{
return obj.Name.GetHashCode();
}
}
Now try this:
var comparer = new MyEqualityComparer();
XDocument.Load(dataSourceFileStream);
var doc = XDocument.Parse(data);
var dataElements = doc.Element("DocumentElement").Elements("data");
foreach (var dataElement in dataElements)
{
var childElements = dataElement.Elements();
var distinctElements = childElements.Distinct(comparer).ToArray();
if (distinctElements.Length != childElements.Count())
{
dataElement.Elements().Remove();
foreach (var item in distinctElements)
dataElement.Add(item);
}
}
using (var stream = new MemoryStream())
{
var writer = new StreamWriter(stream);
doc.Save(writer);
stream.Seek(0, 0);
var ds = new DataSet();
ds.Locale = CultureInfo.InvariantCulture;
var mode = ds.ReadXml(stream);
var dt = ds.Tables[0];
}
That would be a quick workaround to your problem. But i strongly suggest to encourage the data provider to fix the XML
Okay. as stated in my previous comment, you can create your own XmlTextReader which patches/ignores some elements. The idea is, that this reader checks if he has already read an element within the same depth. If it is the case, advance to the end element.
class MyXmlReaderPatcher : XmlTextReader
{
private readonly HashSet<string> _currentNodeElementNames = new HashSet<string>();
public MyXmlReaderPatcher(TextReader reader) : base(reader)
{ }
public override bool Read()
{
var result = base.Read();
if (this.Depth == 1)
{
_currentNodeElementNames.Clear();
}
else if (this.Depth==2 && this.NodeType == XmlNodeType.Element)
{
if (_currentNodeElementNames.Contains(this.Name))
{
var name = this.Name;
do {
result = base.Read();
if (result == false)
return false;
} while (this.NodeType != XmlNodeType.EndElement && this.Name != name);
result = this.Read();
}
else
{
_currentNodeElementNames.Add(this.Name);
}
}
return result;
}
}
All you have to do is to link the new reader in between your ds.ReadXml() and your file stream:
var myReader = new MyXmlReaderPatcher(dataSourceFileStream);
var ds = new DataSet();
ds.Locale = CultureInfo.InvariantCulture;
var mode = ds.ReadXml(myReader);
var dt = ds.Tables[0];
I'm trying to extract some keywords from a text. It works quite fine but I need to remove plurals.
As I'm already using Lucene for searching purpose, I'm trying to use it to extract keyword from indexed terms.
1st, I index the document in a RAMDirectory index,
RAMDirectory idx = new RAMDirectory();
using (IndexWriter writer =
new IndexWriter(
idx,
new CustomStandardAnalyzer(StopWords.Get(this.Language),
Lucene.Net.Util.Version.LUCENE_30, this.Language),
IndexWriter.MaxFieldLength.LIMITED))
{
writer.AddDocument(createDocument(this._text));
writer.Optimize();
}
Then, I extract the keywords:
var list = new List<KeyValuePair<int, string>>();
using (var reader = IndexReader.Open(directory, true))
{
var tv = reader.GetTermFreqVector(0, "text");
if (tv != null)
{
string[] terms = tv.GetTerms();
int[] freq = tv.GetTermFrequencies();
for (int i = 0; i < terms.Length; i++)
list.Add(new KeyValuePair<int, string>(freq[i], terms[i]));
}
}
in the list of terms I can have terms like "president" and "presidents"
How could I remove it?
My CustomStandardAnalyzer use this:
public override TokenStream TokenStream(string fieldName, System.IO.TextReader reader)
{
//create the tokenizer
TokenStream result = new StandardTokenizer(this.version, reader);
//add in filters
result = new Lucene.Net.Analysis.Snowball.SnowballFilter(result, this.getStemmer());
result = new LowerCaseFilter(result);
result = new ASCIIFoldingFilter(result);
result = new StopFilter(true, result, this.stopWords ?? StopWords.English);
return result;
}
So I already use the SnowballFilter (with the correct language specific stemmer).
How could I remove plurals?
My output from the following program is:
text:and
text:presid
text:some
text:text
text:with
class Program
{
private class CustomStandardAnalyzer : Analyzer
{
public override TokenStream TokenStream(string fieldName, System.IO.TextReader reader)
{
//create the tokenizer
TokenStream result = new StandardTokenizer(Lucene.Net.Util.Version.LUCENE_30, reader);
//add in filters
result = new Lucene.Net.Analysis.Snowball.SnowballFilter(result, new EnglishStemmer());
result = new LowerCaseFilter(result);
result = new ASCIIFoldingFilter(result);
result = new StopFilter(true, result, new HashSet<string>());
return result;
}
}
private static Document createDocument(string text)
{
Document d = new Document();
Field f = new Field("text", "", Field.Store.YES, Field.Index.ANALYZED, Field.TermVector.WITH_POSITIONS_OFFSETS);
f.SetValue(text);
d.Add(f);
return d;
}
static void Main(string[] args)
{
RAMDirectory idx = new RAMDirectory();
using (IndexWriter writer =
new IndexWriter(
idx,
new CustomStandardAnalyzer(),
IndexWriter.MaxFieldLength.LIMITED))
{
writer.AddDocument(createDocument("some text with president and presidents"));
writer.Commit();
}
using (var reader = IndexReader.Open(idx, true))
{
var terms = reader.Terms(new Term("text", ""));
if (terms.Term != null)
do
Console.WriteLine(terms.Term);
while (terms.Next());
}
Console.ReadLine();
}
}
I have the XMLDocument and I extract the following xml tags I want using the xmlDocument.SelectSingleNode("./tag") into a string and I want to load it inside a DataTable.
I tried using the dataTable.ReadXML(); but the overloads for this function do not allow a string argument.
Is there a better way of doing this?
Edit : Adding Code
XmlDocument xdoc = new XmlDocument();
xdoc.LoadXml(string_With_Xml);
DataTable accessTable = new DataTable();
accessTable.ReadXml();
I Hope this adds more context to the question.
You can try the following:
//Your xml
string TestSTring = #"<Contacts>
<Node>
<ID>123</ID>
<Name>ABC</Name>
</Node>
<Node>
<ID>124</ID>
<Name>DEF</Name>
</Node>
</Contacts>";
StringReader StringStream = new StringReader(TestSTring);
DataSet ds = new DataSet();
ds.ReadXml(StringStream);
DataTable dt = ds.Tables[0];
you can write extension like this:
public static someType ReadXml(this DataTable dt, string yourParam1, string yourParam2)
{
method body....
}
You can do the following approach, the byte array here can be loaded for a string using for example:
Encoding.UTF8.GetBytes(somestring)
Helper method to load the datatable, note fallback to DataSet's method ReadXml instead of Datatable ReadXml. This will not always be suitable in case your xml contains somehow several data tables, as this method always returns the first data table in the catch:
public DataTable Convert(byte[] bytes)
{
var text = bytes.ToStringUtf8();
if (string.IsNullOrWhiteSpace(text))
{
return null;
}
using (var stream = new MemoryStream(bytes))
{
try
{
var dt = new DataTable();
dt.ReadXml(stream);
return dt;
}
catch (InvalidOperationException ie)
{
Trace.WriteLine(ie);
var ds = new DataSet();
stream.Position = 0;
ds.ReadXml(stream);
if (ds.Tables.Count > 0)
{
return ds.Tables[0];
}
return null;
}
}
}