Shopify的Customer Data新增的富文本类型,开始做API对接时,本以为会很简单,就和描述一样,将html代码编码后直接上传到接口.但是事情没有这么简单…
1.首先这个富文本很奇怪,支持的标签不多
2.接口获取到数据是JSON类型
{"type":"root","children":[{"type":"heading","children":[{"type":"text","value":"text"}],"level":1}]}
3.继续查询接口文档和官方社区,得出结论:Customer Data只支持h标签,p标签,加粗,斜体,a标签,有序列表和无序列表.并且调用接口要按照官方的数据接口上传JSON字符串
下面直接开始放代码:
主程序
using System.Text.RegularExpressions;
using HtmlAgilityPack;
static async Task Main(string[] args)
{
string htmlString = "<h1>标题 1</h1><h2>标题 2</h2><h3>标题 3</h3><h4>标题 4</h4><h5>标题 5</h5><h6>标题 6</h6><p>这是一段话。这是<strong>粗体文本</strong>。这是<em>斜体文本</em>。这是<em><strong>粗斜体文本</strong></em>。这是一个<a href=\"https://example.com/\" target=\"_blank\">链接。</a></p><ul><li>无序列表中的第一个</li><li>无序列表中的第二个</li></ul><p>中间有一段</p><ol><li>有序列表中的第一个</li><li>有序列表中的第二个</li></ol>";
string jsonResult = ShopifyHtmlToJson(htmlString);
Console.WriteLine(jsonResult);
string strHtml = ShopifyJsonToHtml(jsonResult);
Console.ReadLine();
}
将Html转为JSON
public static string ShopifyHtmlToJson(string html)
{
var htmlDoc = new HtmlDocument();
htmlDoc.LoadHtml(html);
RichTextResponse rootNode = new RichTextResponse
{
type = "root",
children = new List<Child>()
};
var firstNodeList = htmlDoc.DocumentNode.ChildNodes;
foreach (var firstNode in firstNodeList)
{
Child child = new Child();
rootNode.children.Add(child);
switch (firstNode.Name.ToLower())
{
case "h1":
child.type = "heading";
child.level = 1;
child.children = new List<Child>();
if (firstNode.HasChildNodes)
{
foreach (var secondNode in firstNode.ChildNodes)
{
child.children.Add(GetChildJson(secondNode));
}
}
else
{
child.children.Add(new Child
{
type = "text",
value = firstNode.InnerHtml
});
}
break;
case "h2":
child.type = "heading";
child.level = 2;
child.children = new List<Child>();
if (firstNode.HasChildNodes)
{
foreach (var secondNode in firstNode.ChildNodes)
{
child.children.Add(GetChildJson(secondNode));
}
}
else
{
child.children.Add(new Child
{
type = "text",
value = firstNode.InnerHtml
});
}
break;
case "h3":
child.type = "heading";
child.level = 3;
child.children = new List<Child>();
if (firstNode.HasChildNodes)
{
foreach (var secondNode in firstNode.ChildNodes)
{
child.children.Add(GetChildJson(secondNode));
}
}
else
{
child.children.Add(new Child
{
type = "text",
value = firstNode.InnerHtml
});
}
break;
case "h4":
child.type = "heading";
child.level = 4;
child.children = new List<Child>();
if (firstNode.HasChildNodes)
{
foreach (var secondNode in firstNode.ChildNodes)
{
child.children.Add(GetChildJson(secondNode));
}
}
else
{
child.children.Add(new Child
{
type = "text",
value = firstNode.InnerHtml
});
}
break;
case "h5":
child.type = "heading";
child.level = 5;
child.children = new List<Child>();
if (firstNode.HasChildNodes)
{
foreach (var secondNode in firstNode.ChildNodes)
{
child.children.Add(GetChildJson(secondNode));
}
}
else
{
child.children.Add(new Child
{
type = "text",
value = firstNode.InnerHtml
});
}
break;
case "h6":
child.type = "heading";
child.level = 6;
child.children = new List<Child>();
if (firstNode.HasChildNodes)
{
foreach (var secondNode in firstNode.ChildNodes)
{
child.children.Add(GetChildJson(secondNode));
}
}
else
{
child.children.Add(new Child
{
type = "text",
value = firstNode.InnerHtml
});
}
break;
case "p":
child.type = "paragraph";
child.children = new List<Child>();
if (firstNode.HasChildNodes)
{
foreach (var secondNode in firstNode.ChildNodes)
{
child.children.Add(GetChildJson(secondNode));
}
}
else
{
child.children.Add(new Child
{
type = "text",
value = firstNode.InnerHtml
});
}
break;
case "ul":
child.type = "list";
child.listType = "unordered";
child.children = new List<Child>();
if (firstNode.HasChildNodes)
{
foreach (var secondNode in firstNode.ChildNodes)
{
child.children.Add(GetChildJson(secondNode));
}
}
else
{
child.children.Add(new Child
{
type = "text",
value = firstNode.InnerHtml
});
}
break;
case "ol":
child.type = "list";
child.listType = "ordered";
child.children = new List<Child>();
if (firstNode.HasChildNodes)
{
foreach (var secondNode in firstNode.ChildNodes)
{
child.children.Add(GetChildJson(secondNode));
}
}
else
{
child.children.Add(new Child
{
type = "text",
value = firstNode.InnerHtml
});
}
break;
}
}
var settings = new JsonSerializerSettings
{
NullValueHandling = NullValueHandling.Ignore,
DefaultValueHandling = DefaultValueHandling.Ignore,
ReferenceLoopHandling = ReferenceLoopHandling.Ignore
};
return JsonConvert.SerializeObject(rootNode, settings);
}
public static Child GetChildJson(HtmlNode htmlNode)
{
Child child = new Child();
switch (htmlNode.Name.ToLower())
{
case "h1":
child.type = " ";
child.level = 1;
child.children = new List<Child>();
if (htmlNode.HasChildNodes)
{
foreach (var firstNode in htmlNode.ChildNodes)
{
child.children.Add(GetChildJson(firstNode));
}
}
else
{
child.children.Add(new Child
{
type = "text",
value = htmlNode.InnerHtml
});
}
break;
case "h2":
child.type = "heading";
child.level = 2;
child.children = new List<Child>();
if (htmlNode.HasChildNodes)
{
foreach (var firstNode in htmlNode.ChildNodes)
{
child.children.Add(GetChildJson(firstNode));
}
}
else
{
child.children.Add(new Child
{
type = "text",
value = htmlNode.InnerHtml
});
}
break;
case "h3":
child.type = "heading";
child.level = 3;
child.children = new List<Child>();
if (htmlNode.HasChildNodes)
{
foreach (var firstNode in htmlNode.ChildNodes)
{
child.children.Add(GetChildJson(firstNode));
}
}
else
{
child.children.Add(new Child
{
type = "text",
value = htmlNode.InnerHtml
});
}
break;
case "h4":
child.type = "heading";
child.level = 4;
child.children = new List<Child>();
if (htmlNode.HasChildNodes)
{
foreach (var firstNode in htmlNode.ChildNodes)
{
child.children.Add(GetChildJson(firstNode));
}
}
else
{
child.children.Add(new Child
{
type = "text",
value = htmlNode.InnerHtml
});
}
break;
case "h5":
child.type = "heading";
child.level = 5;
child.children = new List<Child>();
if (htmlNode.HasChildNodes)
{
foreach (var firstNode in htmlNode.ChildNodes)
{
child.children.Add(GetChildJson(firstNode));
}
}
else
{
child.children.Add(new Child
{
type = "text",
value = htmlNode.InnerHtml
});
}
break;
case "h6":
child.type = "heading";
child.level = 6;
child.children = new List<Child>();
if (htmlNode.HasChildNodes)
{
foreach (var firstNode in htmlNode.ChildNodes)
{
child.children.Add(GetChildJson(firstNode));
}
}
else
{
child.children.Add(new Child
{
type = "text",
value = htmlNode.InnerHtml
});
}
break;
case "p":
child.type = "paragraph";
child.children = new List<Child>();
if (htmlNode.HasChildNodes)
{
foreach (var firstNode in htmlNode.ChildNodes)
{
child.children.Add(GetChildJson(firstNode));
}
}
else
{
child.children.Add(new Child
{
type = "text",
value = htmlNode.InnerHtml
});
}
break;
case "ul":
child.type = "list";
child.listType = "unordered";
child.children = new List<Child>();
if (htmlNode.HasChildNodes)
{
foreach (var firstNode in htmlNode.ChildNodes)
{
child.children.Add(GetChildJson(firstNode));
}
}
else
{
child.children.Add(new Child
{
type = "text",
value = htmlNode.InnerHtml
});
}
break;
case "ol":
child.type = "list";
child.listType = "ordered";
child.children = new List<Child>();
if (htmlNode.HasChildNodes)
{
foreach (var firstNode in htmlNode.ChildNodes)
{
child.children.Add(GetChildJson(firstNode));
}
}
else
{
child.children.Add(new Child
{
type = "text",
value = htmlNode.InnerHtml
});
}
break;
case "li":
child.type = "list-item";
child.children = new List<Child>();
if (htmlNode.HasChildNodes)
{
foreach (var firstNode in htmlNode.ChildNodes)
{
child.children.Add(GetChildJson(firstNode));
}
}
else
{
child.children.Add(new Child
{
type = "text",
value = htmlNode.InnerHtml
});
}
break;
case "strong":
child.type = "text";
child.bold = true;
child.value = htmlNode.InnerHtml;
foreach (var firstNode in htmlNode.ChildNodes)
{
if (firstNode.Name.ToLower() == "em")
{
child.italic = true;
child.value = child.value.Replace("<em>", "");
child.value = child.value.Replace("</em>", "");
}
}
break;
case "em":
child.type = "text";
child.italic = true;
child.value = htmlNode.InnerHtml;
foreach (var firstNode in htmlNode.ChildNodes)
{
if (firstNode.Name.ToLower() == "strong")
{
child.bold = true;
child.value = child.value.Replace("<strong>", "");
child.value = child.value.Replace("</strong>", "");
}
}
break;
case "a":
child.type = "link";
child.target = htmlNode.GetAttributeValue("target", null);
child.url = htmlNode.GetAttributeValue("href", null);
child.title = htmlNode.GetAttributeValue("title", null);
child.children = new List<Child>();
if (htmlNode.HasChildNodes)
{
foreach (var firstNode in htmlNode.ChildNodes)
{
child.children.Add(GetChildJson(firstNode));
}
}
else
{
child.children.Add(new Child
{
type = "text",
value = htmlNode.InnerHtml
});
}
break;
default:
child.type = "text";
child.value = htmlNode.InnerHtml;
break;
}
return child;
}
将JSON转Html
public static string ShopifyJsonToHtml(string json)
{
string strHtml = string.Empty;
RichTextResponse richTextResponse = JsonConvert.DeserializeObject<RichTextResponse>(json);
foreach (var child in richTextResponse.children)
{
strHtml += GetChildHtml(child);
}
return strHtml;
}
public static string GetChildHtml(Child child)
{
string strHtml = string.Empty;
switch (child.type)
{
case "heading":
strHtml += $"<h{child.level}>";
if (child.children.Any())
{
foreach (var firstChild in child.children)
{
strHtml += GetChildHtml(firstChild);
}
}
else
{
strHtml += GetHtmlText(child);
}
strHtml += $"</h{child.level}>";
break;
case "paragraph":
strHtml += $"<p>";
if (child.children.Any())
{
foreach (var firstChild in child.children)
{
strHtml += GetChildHtml(firstChild);
}
}
else
{
strHtml += GetHtmlText(child);
}
strHtml += $"</p>";
break;
case "link":
strHtml += $"<a";
if (!string.IsNullOrEmpty(child.url))
{
strHtml += $" href=\"{child.url}\"";
}
if (!string.IsNullOrEmpty(child.title))
{
strHtml += $" title=\"{child.title}\"";
}
if (!string.IsNullOrEmpty(child.target))
{
strHtml += $" target=\"{child.target}\"";
}
strHtml += ">";
if (child.children.Any())
{
foreach (var firstChild in child.children)
{
strHtml += GetChildHtml(firstChild);
}
}
else
{
strHtml += GetHtmlText(child);
}
strHtml += $"</a>";
break;
case "list":
if (child.listType == "unordered")
{//无序列表
strHtml += "<ul>";
if (child.children.Any())
{
foreach (var firstChild in child.children)
{
strHtml += GetChildHtml(firstChild);
}
}
else
{
strHtml += GetHtmlText(child);
}
strHtml += "</ul>";
}
else if (child.listType == "ordered")
{//有序列表
strHtml += "<ol>";
if (child.children.Any())
{
foreach (var firstChild in child.children)
{
strHtml += GetChildHtml(firstChild);
}
}
else
{
strHtml += GetHtmlText(child);
}
strHtml += "</ol>";
}
break;
case "list-item":
strHtml += "<li>";
if (child.children.Any())
{
foreach (var firstChild in child.children)
{
strHtml += GetChildHtml(firstChild);
}
}
else
{
strHtml += GetHtmlText(child);
}
strHtml += "</li>";
break;
default:
strHtml += GetHtmlText(child);
break;
}
return strHtml;
}
public static string GetHtmlText(Child child)
{
string strHtml = $"{(child.bold ? "<strong>" : "")}{(child.italic ? "<em>" : "")}{child.value}{(child.bold ? "</strong>" : "")}{(child.italic ? "</em>" : "")}";
return strHtml;
}
实体类
public class RichTextResponse
{
public string type { get; set; }
public List<Child> children { get; set; }
}
public class Child
{
public string type { get; set; }
public string value { get; set; }
public bool bold { get; set; }
public bool italic { get; set; }
public string url { get; set; }
public string title { get; set; }
public string target { get; set; }
public List<Child> children { get; set; }
public int level { get; set; }
public string listType { get; set; }
}