C#Html回転JSONを実現



Htmlはツリー構造→Jsonは配列構造
シーンの適用
H 5やブラウザでHtmlコードを表示するのは問題ありませんが、オリジナルアプリやReactNativeで直接Htmlを表示させるのは不便かもしれません
実装方法
正規表現でキャプチャ、置換は可能ですが、テキストタイプが複雑であれば正規表現の複雑さは直線的に上昇するので、ここではより柔軟な実装を考える
1 Html文字列テキストをツリー構造オブジェクトHtmlNodeに解析
正規表現またはサードパーティ製フレームワークを使用して、各ノードの先頭が不明であることを決定し、位置決めします.
メソッド正規表現の一致
正則位置決めラベル位置、及び全ての属性を用いて、仮ツリー構造HtmlTagを生成し、対象はラベル開始位置、属性と本文内容を含み、次の加工に使用する.
自分でこのような正則的なマッチングを実現するには強い正則的な基礎が必要で、正則的によく知らない学生は放棄して、無駄な時間を避けて、最後に使えないコードを実現してください.
メソッド2サードパーティHtmlオブジェクトの転送
現在、C#、JAVAにはオープンソースのHtmlツリーオブジェクトコード倉庫があります.
利点は、成熟したtokenize、すべてのhtmlラベルが規範化されていない場合に完璧に一致し、1つのコードはCQ dom = CQ.Create(html);を実現し、nugetでインストールすることができます
欠点、しばらく発見していないで、もし解析の誤った問題が現れるならば、後でのは使うことができないかもしれません
すべての要素または場所を再帰的に巡回し、HtmlNodeツリーに変換し、有用な情報を抽出し、不要な情報をフィルタします.
domツリー構造を順次巡回し、HtmlNodeを生成
private HtmlNode IterateIDOMElement(IDomObject element, HtmlNode parent)
{
    HtmlNode current;
    if (parent == null) //root  
    {
        current = HtmlNode.CreateRoot();
    }
    else
    {
        if (element.NodeName == "#text")
        {
            current = HtmlNode.CreatePlainTag(element.ToString());
        }
        else
        {
            //          ,           
            var attrs = new StringBuilder();
            string link = null;
            if (element.HasAttributes)
            {
                element.Attributes.ToList().ForEach(x => attrs.AppendFormat("{0}='{1}' ", x.Key, x.Value));
                var href = element.Attributes.FirstOrDefault(x => x.Key.ToLower() == "href");
                link = href.Value;
            }
            current = HtmlNode.Create(attrs.ToString(), NameToTagType(element.NodeName));
            current.link = link;
        }
        parent.AddChild(current);
    }
    //    
    if (element.HasChildren)
    {
        for (var i = 0; i < element.ChildNodes.Count; i++)
        {
            IterateIDOMElement(element[i], current);//       
        }
    }
    return current;
}

NameToTagTypeメソッドでは、ラベルをどのようなタイプのラベルに変換するか(改行、デフォルトテキスト、ピクチャなど)をカプセル化します.
public enum TagType
{
    Breaking,
    Image,
    Default
}
private static TagType NameToTagType(string tagname) 
{
    if(string.IsNullOrEmpty(tagname)) return TagType.Default;
    switch (tagname.ToLower().Trim())
    {
        case "p":
        case "br":
        {
            return TagType.Breaking;
        }
        case "img":
        {
            return TagType.Image;
            }
        default:
            return TagType.Default;
    }
}

HtmlNodeノードのプロパティの説明
public class HtmlNode
{
    private TagType type { get; set; }//    
    private string properties { get; set; }//    
    private string content { get; set; }//      (   )
    //     ,              ,           ,          
    private readonly List ChildTags = new List();
    private string _link;//    
    public stirng link{
        private get
        {
            //...
        }
        set { _link = value; }
    }
    private string src//    URL
    {
        get
        {
            //...
        }
    }
}

HtmlNodeに組み込まれた作成ヘルプ
public static HtmlNode Create(string properties, TagType type, string content = null){
    return new HtmlNode()
    {
        type = type,
        properties = properties,
        content = content
    };
}
public static HtmlNode CreateRoot(){
    return Create(null, TagType.Default);
}
public static HtmlNode CreatePlainTag(string text){
    return Create(null, TagType.Default, text);
}
public void AddChild(HtmlNode child){
    if (child == null) return;
    if (child.type == TagType.Default && !string.IsNullOrEmpty(child.content))
        child.content = child.content.Replace(" ", " ");
    ChildTags.Add(child);
}

2後順にHtmlNodeツリーを巡回し、各ノードを翻訳し、リストを生成する
最初のステップが完了するとルートノードが得られ、その後ルートノードを遍歴してListリストに変換されます.List returnList = mockHtmlTag.BackOrderTravel();//
ステップ2以降は、すべてのリーフノードを配列に追加する前にアクセスし、最後にこのノードの追加にアクセスします.
BackOrderTravelメソッド
図のように、まずリーフノードをListオブジェクトに変換し、上位層は直接次の層のリストオブジェクトと本層のオブジェクトを処理し、上位層に戻る
再帰的な論理:
1、サブノードがある場合はサブノードを後回しにしてリストを生成して2を返し、サブノードがない場合は本ノードをリストオブジェクトにして3を返し、サブノードがある場合は本ノードにも内容がある場合は、本ノードがサブノードリストとマージできるかどうかをチェックし、マージできる場合はマージを行う.リスト後部4にマージできないHtmlラベルのリンク、スタイルの場合、上位リンクまたはスタイルは下位リンクまたはスタイルに影響しますが、下位に同じリンクまたはスタイルがある場合は、下位リンクまたはスタイルは保持されます
List reList = new List();
//    
if (ChildTags != null && ChildTags.Count > 0)
    foreach (var r in ChildTags) 
        AddClientContentItems(ref reList, r.BackOrderTravel(),this.link);
//     
ClientContentItem item = TransCurrentNodeToClientContentItem();
//     
if (item != null)
{
    if (reList.Count > 0)
    {
        var lastitem = reList.Last();
        if (lastitem.JoinAbleWith(item))
        {
            lastitem.JoinWith(item);
        }
        else
        {
            reList.Add(item);
        }
    }
    else
    {
        reList.Add(item);
    }        
}
return reList;

AddClientContentItemsメソッド:サブノードで生成されたListを順次全体Listに追加
private void AddClientContentItems(ref List reList, List backOrderTravel,string olink)
{
    if (backOrderTravel == null || backOrderTravel.Count == 0) return;
    //  Link      
    if (!string.IsNullOrEmpty(olink))
    {
        backOrderTravel.Where(r=>r.TextType == ClientTextType.Text).ToList().ForEach(x=>
        {
            x.TextType = ClientTextType.Href;
            x.Link = olink;
        });
        backOrderTravel.Where(r=>r.TextType == ClientTextType.Photo).ToList().ForEach(x=>x.Link = olink);
    }

    if (reList == null || reList.Count == 0)
    {
        reList = backOrderTravel;
        return;
    }
    //   list    ,        item  ,     
    var lastitem = reList.Last();
    foreach (ClientContentItem curItem in backOrderTravel)
    {
        if (lastitem.JoinAbleWith(curItem))
        {
            lastitem.JoinWith(curItem);
        }
        else
        {
            lastitem = curItem;
            reList.Add(lastitem);
        }
    }
}

TransCurrentNodeToClientContentItemメソッド:このノードをlistItemに変換する
private ClientContentItem TransCurrentNodeToClientContentItem()
{
    if (string.IsNullOrEmpty(content) && string.IsNullOrEmpty(link) && this.type == TagType.Default)
        return null;
    var currentItem = new ClientContentItem();
    if (this.type == TagType.Image)
    {
        currentItem.Text = src;//TODO:  onclick src
        currentItem.TextType = ClientTextType.Photo;
        if (!string.IsNullOrEmpty(link)) currentItem.Link = link;
    }
    else if (this.type == TagType.Breaking)
    {
        if (!string.IsNullOrEmpty(content))
            currentItem.Text = content;
        else
            currentItem.Text = string.Empty;
        currentItem.Text += "\r
"; currentItem.TextType = ClientTextType.Text; if (!string.IsNullOrEmpty(link)) { currentItem.Link = link; currentItem.TextType = ClientTextType.Href; } } else if (this.type == TagType.Default && !string.IsNullOrEmpty(link)) { if (!string.IsNullOrEmpty(content)) currentItem.Text = content; currentItem.Link = link; currentItem.TextType = ClientTextType.Href; } else { if (!string.IsNullOrEmpty(content)) currentItem.Text = content; currentItem.TextType = ClientTextType.Text; } return currentItem; }

JoinAbleWithメソッド:リンクが同じ、またはリンクされていないテキストが1つの要素に結合できるなど、2つのListItemが結合できるかどうかを確認します.
public bool JoinAbleWith(ClientContentItem curItem)
{
    if (this.TextType == curItem.TextType)
    {
        switch (this.TextType)
        {
            case ClientTextType.Href:
            {
                return this.Link == curItem.Link;
            }
            case ClientTextType.Text:
            {
                return true;
            }
        }
    }
    return false;
}

JoinWithメソッド:2つのListItemをマージ
public void JoinWith(ClientContentItem curItem)
{
    if (this.TextType == curItem.TextType)
    {
        switch (this.TextType)
        {
            case ClientTextType.Href:
            {
                if (this.Link == curItem.Link)
                {
                    this.Text = (this.Text ?? String.Empty) + curItem.Text;
                }
                break;
            }
            case ClientTextType.Text:
            {
                this.Text = (this.Text ?? String.Empty) + curItem.Text;
                break;
            }
        }
    }
}

3仕上げ
リストオブジェクトが生成されましたので、生成されたリストオブジェクトを再処理し、互換性のある操作を完了できます.
例えば、一般的にリッチテキストのメンテナンスではPラベルがセットされていますが、Pラベルの末尾は改行記号rで、このときは最後尾の改行記号を削除して、展示に影響を与えないようにすることができます.
//List      
if (returnList != null && returnList.Count > 0)
{
    //          
    while (returnList.Count > 0 && !string.IsNullOrEmpty(returnList.Last().Text) && returnList.Last().Text.EndsWith("\r
")) { returnList.Last().Text = returnList.Last().Text.Substring(0, returnList.Last().Text.LastIndexOf("\r
", StringComparison.Ordinal)); if (string.IsNullOrEmpty(returnList.Last().Text) && returnList.Last().TextType == ClientTextType.Text) { returnList.RemoveAt(returnList.Count - 1); } } for (int i = 0; i < returnList.Count - 1; i++) { if (returnList[i].TextType == ClientTextType.Photo && returnList[i + 1].TextType == ClientTextType.Href && !string.IsNullOrEmpty(returnList[i + 1].Link) && string.IsNullOrEmpty(returnList[i + 1].Text)) { returnList[i].Link = returnList[i + 1].Link; } } // , Type foreach (var clientOntentItem in returnList.Where(r => r.TextType == ClientTextType.Href && !string.IsNullOrEmpty(r.Link))) { if (clientOntentItem.Link.ToLower().Contains("mailto:")) { clientOntentItem.Link = string.Empty; clientOntentItem.TextType = ClientTextType.Email; } if (clientOntentItem.Link.ToLower().Contains("telto:")) { clientOntentItem.Link = string.Empty; clientOntentItem.TextType = ClientTextType.Tel; } } }

JAVA移転
CsQueryが使用するHtmlParserはJavaから移行したThe Validator.nu HTML Parser
このほかmavenにはHTML Parser JarとJericho HTML Parserがあります
Javaはツリー解析をするときに参照できます
jsoupとCSQueryが最も近い:
String html = "  ,      ここ ssssssss。";
Document doc = Jsoup.parse(html);
Elements body = doc.select("body");

JavaソースリファレンスC#ソースリファレンス
転載先:https://www.cnblogs.com/enigmaxp/p/8351063.html