Replacing tags in HtmlAgility - html-agility-pack

I'm trying to replace all of my h1 tags with h2 tags and I'm using HtmlAgility pack.
I did this:
var headers = doc.DocumentNode.SelectNodes("//h1");
if (headers != null)
{
foreach (HtmlNode item in headers)
{
//item.Replace??
}
}
and i got stuck there. I've tried this:
var headers = doc.DocumentNode.SelectNodes("//h1");
if (headers != null)
{
foreach (HtmlNode item in headers)
{
HtmlNode newNode = new HtmlNode(HtmlNodeType.Element, doc, item.StreamPosition);
newNode.InnerHtml = item.InnerHtml;
// newNode suppose to set to h2
item.ParentNode.ReplaceChild(newNode, item);
}
}
problem there is that i have no idea how to create a new h2, get all the attributes etc.
i'm sure theres a simple way to do that, any ideas?

var headers = doc.DocumentNode.SelectNodes("//h1");
if (headers != null)
{
foreach (HtmlNode item in headers)
{
item.Name = "h2"
}
}

A similar approach replacing the tags using Descendants instead of SelectNodes:
IEnumerable<HtmlNode> tagDescendants = doc.DocumentNode.Descendants("h1");
foreach (HtmlNode htmlNode in tagDescendants)
{
htmlNode.Name = "h2";
}

Related

Filter on Tree or Nested Data

I have seen in one of the issues "Filter on Tree or Nested Data #1562" Oli has mentioned that
Hey #fr0z3nfyr
Filtering is supported on tree child nodes since version 4,2
Cheers
Oli :)
I am unable to find any example or the code to search nested data.
My code works perfectly fine for flat tables, but with nested tables it only works for the root node.
//data - the data for the row being filtered
//filterParams - params object passed to the filter
var match = false;
for (var key in data) {
if (data[key] != null) {
if ((data[key]).indexOf(filterParams.value) != -1) {
match = true;
}
}
}
return match;
}
function updateFilter(){
if ($("#filter-field").val() == "All Columns") {
table.setFilter(matchAny,{ value: $("#filter-value").val()});
} else {
table.setFilter($("#filter-field").val(), "like", $("#filter-value").val());
}
//var filter = $("#filter-field").val() == "All Columns" ? matchAny : $("#filter-field").val() ;
}```
Oli could you please point me to an example where Nested data filtering is supported
I was able to solve this, but by re-setting table data with filtered value and also the tree structure is not maintained in the filtered list. I can maintain the tree structure with some changes in code, but this flat looks more like what I needed once filtering is done.
// This method iterates through the dataRows and its tree children and call a recursive function which creates the filtered table data.
function updateFilter() {
var filtertableData = [];
table.getRows().filter(function (row) {
var rootData = row.getData();
rootData._children = [];
matchData(rootData, filtertableData);
var childRows = row.getTreeChildren();
searchForChildRows(rootData,childRows,filtertableData);
while (childRows.length != 0) {
for (var i = 0; i < childRows.length; i++) {
var childrow = childRows[i];
var childData = childrow.getData();
childData._children = [];
childRows = childrow.getTreeChildren();
searchForChildRows(childData,childRows,filtertableData);
}
}
});
table.setData(filtertableData);
}
function matchData(rootData, filtertableData, childdata) {
if (typeof childdata === "undefined") {
for (var key in rootData) {
console.log(key);
console.log(allVisibleCBSCols);
if (rootData[key] != null && typeof rootData[key] == 'string' && allVisibleCBSCols.includes(key)) {
if ((rootData[key]).indexOf($("#filter-value-Project").val()) != -1) {
filtertableData.push(rootData);
break;
}
}
}
} else {
for (var key in childdata) {
if (childdata[key] != null && typeof childdata[key] == 'string' && allVisibleCBSCols.includes(key)) {
if ((childdata[key]).indexOf($("#filter-value-Project").val()) != -1) {
//rootData._children.push(childdata);
filtertableData.push(childdata);
break;
}
}
}
}
}
function searchForChildRows(rootData,childRows,filtertableData) {
for (var i = 0; i < childRows.length; i++) {
var childrow = childRows[i];
var childData = childrow.getData();
childData._children = [];
matchData(rootData,filtertableData,childData);
}
}

How to check nod exist in html page?

i have html page and parsing with htmlagilitypack
i manualy change how value for node exist but i need automatic change by value 'statusAct' or 'statusInac'
if (checkBox1.Checked == true)
{
foreach (HtmlNode text in htmlDoc.DocumentNode.SelectNodes("//table[#id='rTbl']/tr/td/span[#class='statusAct']/a[#title='Status Definitions']/text()"))
{
dataGridView1.Rows[n].Cells[1].Value = text.InnerText;
i++;
}
}
else if (checkBox2.Checked == true)
{
foreach (HtmlNode text in htmlDoc.DocumentNode.SelectNodes("//table[#id='rTbl']/tr/td/span[#class='statusInac']/a[#title='Status Definitions']/text()"))
{
dataGridView1.Rows[n].Cells[1].Value = text.InnerText;
i++;
}
}

How can I check for a response cookie in Asp.net Core MVC (aka Asp.Net 5 RC1)?

I'm converting a web forms application to asp.net core mvc. In my web forms application sometimes after I set some response cookies other code needs to see if they were set, and if so, access the cookie's properties (i.e. value, Expires, Secure, http). In webforms and MVC 5 it's possible to iterate over the cookies and return any particular cookies like so (old school I know)
for(int i = 0; i < cookies.Count; i++) {
if(cookies[i].Name == cookieName) {
return cookies[i];
}
}
But the interface for accessing response cookies in asp.net core mvc looks like this:
Based on this interface I don't see a way to check to see if a response cookie exists and obtain it's properties. But there has gotta be some way to do it?
In an action method I tried setting two cookies on the response object and then immediately trying to access them. But intellisense doesn't show any methods, properties or indexers that would allow me to access them:
For a moment, I thought that perhaps I could use Response.Cookies.ToString(); and just parse the information to find my cookie info, but alas, the ToString() call returns "Microsoft.AspNet.Http.Internal.ResponseCookies" because the object doesn't override the default implementation.
Just for fun I also checked the current dev branch of GitHub to see if the interface has changed since RC1 but it has not. So given this interface, how do I check for the existence of a response cookie and get it's properties? I've thought about trying to hack in via the response headers collection but that seems pretty lame.
There is an extension method available in Microsoft.AspNetCore.Http.Extensions called GetTypedHeaders(). This can be called on HttpContext.Response to read Set-Cookie headers. For example in middleware perhaps we want to intercept a Set-Cookie response header and replace it:
public async Task Invoke(HttpContext httpContext)
{
httpContext.Response.OnStarting(state =>
{
var context = (HttpContext)state;
var setCookieHeaders = context.Response.GetTypedHeaders().SetCookie;
// We assume only one cookie is found. You could loop over multiple matches instead.
// setCookieHeaders may be null if response doesn't contain set-cookie headers
var cookie = setCookieHeaders?.FirstOrDefault(x => x.Name == "mycookiename");
if (cookie == null)
{
return Task.CompletedTask;
}
var opts = new CookieOptions
{
HttpOnly = true,
Expires = DateTimeOffset.Now.AddHours(12),
SameSite = SameSiteMode.Lax,
Secure = true
};
context.Response.Cookies.Delete(cookie.Name.Value);
context.Response.Cookies.Append(cookie.Name.Value, "mynewcookievalue", opts);
return Task.CompletedTask;
}, httpContext);
await _next(httpContext);
}
Here's how I get the value of a cookie from a Response. Something like this could be used to get the whole cookie if required:
string GetCookieValueFromResponse(HttpResponse response, string cookieName)
{
foreach (var headers in response.Headers.Values)
foreach (var header in headers)
if (header.StartsWith($"{cookieName}="))
{
var p1 = header.IndexOf('=');
var p2 = header.IndexOf(';');
return header.Substring(p1 + 1, p2 - p1 - 1);
}
return null;
}
There is an obvious problem with Shaun's answer: it will match any HTTP-header having matching value. The idea should be to match only cookies.
A minor change like this should do the trick:
string GetCookieValueFromResponse(HttpResponse response, string cookieName)
{
foreach (var headers in response.Headers)
{
if (headers.Key != "Set-Cookie")
continue;
string header = headers.Value;
if (header.StartsWith($"{cookieName}="))
{
var p1 = header.IndexOf('=');
var p2 = header.IndexOf(';');
return header.Substring(p1 + 1, p2 - p1 - 1);
}
}
return null;
}
Now the checking for a cookie name will target only actual cookie-headers.
You can use this function to get full information about the cookie set value
private SetCookieHeaderValue GetCookieValueFromResponse(HttpResponse response, string cookieName)
{
var cookieSetHeader = response.GetTypedHeaders().SetCookie;
if (cookieSetHeader != null)
{
var setCookie = cookieSetHeader.FirstOrDefault(x => x.Name == cookieName);
return setCookie;
}
return null;
}
In my case I had to get only value of auth= and don't care about other values, but you can easy rewrite code to get dict of cookie values.
I wrote this extension for HttpResponseMessage, in order to avoid work with index and substring, I separate string to array and then cast it to dictionary
internal static string GetCookieValueFromResponse(this HttpResponseMessage httpResponse, string cookieName)
{
foreach (var cookieStr in httpResponse.Headers.GetValues("Set-Cookie"))
{
if(string.IsNullOrEmpty(cookieStr))
continue;
var array = cookieStr.Split(';')
.Where(x => x.Contains('=')).Select(x => x.Trim());
var dict = array.Select(item => item.Split(new[] {'='}, 2)).ToDictionary(s => s[0], s => s[1]);
if (dict.ContainsKey(cookieName))
return dict[cookieName];
}
return null;
}
You can get the cookie string value from the response using this method. In this case, I'm using this logic in a CookieService, that is the reason you will see that I'm using the _contextAccessor. Notice that in this case, I'm returning null if the value of the cookie is empty. (This is optional)
private string GetCookieFromResponse(string cookieName)
{
var cookieSetHeader = _contextAccessor.HttpContext.Response.GetTypedHeaders().SetCookie;
var cookie = cookieSetHeader?.FirstOrDefault(x => x.Name == cookieName && !string.IsNullOrEmpty(x.Value.ToString()));
var cookieValue = cookie?.Value.ToString();
if (!string.IsNullOrEmpty(cookieValue))
{
cookieValue = Uri.UnescapeDataString(cookieValue);
}
return cookieValue;
}
If you are looking for a solution that gets the cookie from the response or the request in that order then you can use this.
private string GetCookieString(string cookieName)
{
return GetCartCookieFromResponse(cookieName) ?? GetCartCookieFromRequest(cookieName) ?? "";
}
private string GetCookieFromRequest(string cookieName)
{
return _contextAccessor.HttpContext.Request.Cookies.ContainsKey(cookieName) ? _contextAccessor.HttpContext.Request.Cookies[cookieName] : null;
}
private string GetCookieFromResponse(string cookieName)
{
var cookieSetHeader = _contextAccessor.HttpContext.Response.GetTypedHeaders().SetCookie;
var cookie = cookieSetHeader?.FirstOrDefault(x => x.Name == cookieName && !string.IsNullOrEmpty(x.Value.ToString()));
var cookieValue = cookie?.Value.ToString();
if (!string.IsNullOrEmpty(cookieValue))
{
cookieValue = Uri.UnescapeDataString(cookieValue);
}
return cookieValue;
}
The snippet seems to work for me. The result for me (derived from Shaun and Ron) looks like:
public static string GetCookieValueFromResponse(HttpResponse response, string cookieName)
{
// inspect the cookies in HttpResponse.
string match = $"{cookieName}=";
var p1 = match.Length;
foreach (var headers in response.Headers)
{
if (headers.Key != "Set-Cookie")
continue;
foreach (string header in headers.Value)
{
if (header.StartsWith(match) && header.Length > p1 && header[p1] != ';')
{
var p2 = header.IndexOf(';', p1);
return header.Substring(p1 + 1, p2 - p1 - 1);
}
}
}
return null;
}
In cases where multiple "Set-Cookie" headers exists the last one should be used:
private string GetCookieValueFromResponse(Microsoft.AspNetCore.Http.HttpResponse response, string cookieName)
{
var value = string.Empty;
foreach (var header in response.Headers["Set-Cookie"])
{
if (!header.Trim().StartsWith($"{cookieName}="))
continue;
var p1 = header.IndexOf('=');
var p2 = header.IndexOf(';');
value = header.Substring(p1 + 1, p2 - p1 - 1);
}
return value;
}

Attribute alignment after edit in AutoCAD

I have a simple routine that updates the text value of an attributereference. After the routine runs the values are updated in the drawing but the text is left justified and not centered. I have not been able to find any command that will cause AutoCAD to update the text location. So any help would be appreciated.
My Code
using (Transaction acTrans = db.TransactionManager.StartTransaction())
{
BlockTable bt = (BlockTable)acTrans.GetObject(db.BlockTableId, OpenMode.ForRead);
foreach (ObjectId oid in bt)
{
BlockTableRecord btr = (BlockTableRecord)acTrans.GetObject(oid, OpenMode.ForRead);
foreach (ObjectId o in btr)
{
if (o.ObjectClass.Name == "AcDbBlockReference")
{
BlockReference br = (BlockReference)acTrans.GetObject(o, OpenMode.ForRead);
BlockTableRecord b2 = (BlockTableRecord)acTrans.GetObject(br.BlockTableRecord, OpenMode.ForRead);
if (b2.Name == blockName)
{
AttributeCollection ac = br.AttributeCollection;
foreach (ObjectId i in ac)
{
AttributeReference ar = (AttributeReference)acTrans.GetObject(i, OpenMode.ForWrite);
string tagName = ar.Tag;
foreach (TestAutoCADCntrl.CBAttributeTag t in tags)
{
if (t.TagName == tagName)
{
ar.Justify = AttachmentPoint.MiddleCenter;
ar.AdjustAlignment(db);
ar.TextString = t.TagValue;
ar.DowngradeOpen();
}
}
}
br.RecordGraphicsModified(true);
}
}
}
}
acTrans.Commit();
Sorry, I have been searching for this issue for 3 days and found the answer right after i posted this question. For anyone else you just need to change the working database before you update the attribute text value.
foreach (TestAutoCADCntrl.CBAttributeTag t in tags)
{
if (t.TagName == tagName)
{
Database wdb = HostApplicationServices.WorkingDatabase; HostApplicationServices.WorkingDatabase = db;
ar.TextString = t.TagValue;
ar.AdjustAlignment(db);
HostApplicationServices.WorkingDatabase = wdb;
}
}

DataTable that has List<string> in it

I'm using Linq to return data from an XML file to a DataTable and that works. But now I'm trying to modify the code to loop through the DataTable. I created a custom class to model the data and have a List in my model. When I loop through the DataTable to display records it displays System.Collections.Generic.List`1[System.String] instead of the actual data. I'm not sure what to search to find the answer.
Snippet:
foreach (DataRow row in tbl.Rows)
{
myList = row["myList"] + ", " + myList;
}
The myList column is the List. I hope this makes sense.
Edited:
public static DataTable LINQToDataTable<T>(IEnumerable<T> varlist)
{
DataTable dtReturn = new DataTable();
// column names
PropertyInfo[] oProps = null;
if (varlist == null) return dtReturn;
foreach (T rec in varlist)
{
// Use reflection to get property names, to create table, Only first time, others will follow
if (oProps == null)
{
oProps = ((Type)rec.GetType()).GetProperties();
foreach (PropertyInfo pi in oProps)
{
Type colType = pi.PropertyType;
if ((colType.IsGenericType) && (colType.GetGenericTypeDefinition() == typeof(Nullable<>)))
{
colType = colType.GetGenericArguments()[0];
}
dtReturn.Columns.Add(new DataColumn(pi.Name, colType));
}
}
DataRow dr = dtReturn.NewRow();
foreach (PropertyInfo pi in oProps)
{
dr[pi.Name] = pi.GetValue(rec, null) == null ?DBNull.Value :pi.GetValue
(rec,null);
}
dtReturn.Rows.Add(dr);
}
return dtReturn;
}
hobbiesList = (List<string>)row["hobbies"];
foreach (var item in hobbiesList)
{
hobbies.Add(item.ToString());
}
hobby = string.Join(", ", hobbies.ToArray());
hobbies.Clear();
I had to do the above to get it to work. I then add hobby to my output array.

Resources