将类似 DOM 的结构转换为降价的有效方法

Efficient way to convert DOM-like structure to markdown

所以我有一个类似 DOM 的树,我正在尝试将其转换为 markdown。例如它可以看起来像这样

[
   {
      'type': 'header',
      'attr': {
         'size': 2
      },
      'children': [
         'A header, ',
         {
            'type': 'link',
            'attr': {
               'url': 'https://www.google.com'
            },
            'children': 'a link inside a header'
         }
      ]
   },
   'some more text'
]

我想要的输出是

## A header, [a link inside a header](https://www.google.com)
some more text

我试过了

def genMd(tree):
   md_string = ''

   for element in tree:
      if type(element) == str:
         md_string += element

      elif type(element) == dict:
         if element['type'] == 'header':
            md_string += '{} {}\n'.format('#' * element['attr']['size'], genMd(element['children']))
 
         elif element['type'] == 'link':
            md_string += '[{}]({})'.format(genMd(element['children']), element['attr']['url']

         # I would add more if statements here for the other cases

   return md_string

这行得通,但似乎效率很低,我最终会得到大量的 if 语句。我也试过这个

def genMd(tree):
   MD_TABLE = {
      'header': '\'{} {}\n\'.format(\'#\' * element[\'attr\'][\'size\'], genMd(element[\'children\']))',
      'link': '\'[{}]({})\'.format(genMd(element[\'children\']), element[\'attr\'][\'url\'])'
      # More entries here for the other cases
   }

   md_string = ''

   for element in tree:
      if type(element) == str:
         md_string += element
      
      elif type(element) == dict:
         md_string += eval(MD_TABLE[element['type']])

   return md_string

也能用,但感觉还是不对。

TL;DR:使用 if 语句感觉不对,有更好的方法吗?

另一种方法可能包括使用生成器函数遍历 DOM 树,同时保留单独的函数来处理各种类型的特定格式:

def markdown(d):
   def m_header(a):
      yield f"{'#'*a['attr']['size']} "+' '.join(markdown(a['children']))
   def m_link(a):
      yield f'[{" ".join(markdown(a["children"]))}]({a["attr"]["url"]})'
   types = {'header':m_header, 'link':m_link}
   for i in ([d] if not isinstance(d, list) else d):
      if not isinstance(i, dict):
         yield i
      else:
         yield from types[i['type']](i)

dom = [{'type': 'header', 'attr': {'size': 2}, 'children': ['A header, ', {'type': 'link', 'attr': {'url': 'https://www.google.com'}, 'children': 'a link inside a header'}]}, 'some more text']
print('\n'.join(markdown(dom)))

输出:

## A header,  [a link inside a header](https://www.google.com)
some more text

一些观察:

  1. 通过使用带有 str.join 的生成器,您不需要通过 += 连续连接字符串,从而使代码更清晰并且 increased efficiency.
  2. 使用函数为特定类型生成标记比使用带有 eval 的字符串格式更易于维护和更安全。