qt读写xml文件(DOM和SAX两种方式)

news2024/9/29 19:25:00

一、XML简介:

      XML, 全称为扩展标记语言, 可用来标记数据、定义数据类型,是一种允许用户对自己的标记语言进行定义的源语言。XML非常适合万维网传输,提供统一的方法来描述和交换独立于应用程序或供应商的结构化数据,是Internet环境中跨平台的、依赖于内容的技术,也是当今处理分布式结构信息的有效工具。XML常用于接口调用,配置文件,数据存储等场景。

二、XML解析技术:

1)dom (Document Object Model)

      将整个文档当作一个对象装入内存进行处理,根据xml的层级结构,在内存中分配一个树形结构,把xml中的每部分都封装成对象,开发者可以访问这个对象中得每一个节点,每一个节点对应XML文件里得一个标记。

      优点:方便实现增删查改操作
      缺点:如果文件过大时,造成内存溢出

      实现类:QDomDocument
      .Pro文件加上 QT += xml

      使用示例:
            QDomDocument doc(“mydocument”);
            QFile file(“mydocument.xml”);
            if (!file.open(QIODevice::ReadOnly))
            return;
            if (!doc.setContent(&file))
            {
                 file.close();
                 return;
            }
            file.close();

            通过setContent将指定的内容指定给QDomDocument解析,解析成功得到一个完整的QDomDocument对象doc,只要访问doc的节点就能得到XML上的内容。

2)sax (Simple API for XML)

      按阶段将文档读取到内存中,进行边读边解析,从上到下,一行一行解析对象,返回对象名称。

      优点:不存在内存溢出,方便实现查询
      缺点:不便于随机访问任意节点

      实现类:QXmlSimpleReader

      使用示例:
            QFile file(“mydocument.xml”);
            QXmlStreamReader xmlRead; //创建一个类对象
            xmlRead.setDevice(&file); //通过setDevice设置要处理的XML文件
            if(xmlRead.readNextStartElement()) //挨个读入节点
            {
                if(xmlRead.name().toString() == “root”)
                {
                    if(xmlRead.readNextStartElement())
                    {
                        if(xmlRead.name().toString() == “config”)
                        {
                            …
                        }
                    }
                }
            }

三、CSV与XML的区别:

1)CSV

      格式语法不同,CSV每条记录占一行,以逗号为分隔符,逗号前后的空格会被忽略。
CSV格式很简单。 它具有包含类别和术语及其属性值(例如描述,缩写和自定义属性值)的功能。 它还使您能够定义管家关系。

2)XML

      XML格式更全面,更复杂。 它具有定义术语,类别和其他对象类型(包括与其他术语有关的术语,与术语有关的类别,链接到已分配资产的术语)之间的每种可能关系的功能。

四、XML读写demo:

1)界面

在这里插入图片描述

2)核心代码

ctxml.h

#ifndef CTXML_H
#define CTXML_H
#include <QObject>
#include "commondef.h"

class CtXml : public QObject
{
    Q_OBJECT
public:
    CtXml();
    ~CtXml();
    static CtXml& getInstance();
signals:
    void sig_returnXmlData(PERSION_T stPersion);

public slots:
    void on_writeToXmlFileWithDom(PERSION_T stPersion);
    void on_readFromXmlFileWithDom();

    void on_writeToXmlFileWithSax(PERSION_T stPersion);
    void on_readFormXmlFileWithSax();
};

#endif // CTXML_H

ctxml.cpp

#include "ctxml.h"
#include <QDomDocument>
#include <QFile>
#include <QXmlStreamReader>
#include <QXmlStreamWriter>

CtXml::CtXml()
{
}

CtXml::~CtXml()
{
}

CtXml &CtXml::getInstance()
{
    static CtXml s_obj;
    return s_obj;
}

void CtXml::on_writeToXmlFileWithDom(PERSION_T stPersion)
{
    QDomDocument dDoc;
    QString sHeader("version=\"1.0\" encoding=\"UTF-8\"");
    dDoc.appendChild(dDoc.createProcessingInstruction("xml", sHeader));//生成说明
    //生成根节点
    QDomElement sRootElement = dDoc.createElement("Root");
    dDoc.appendChild(sRootElement);

    QDomElement PersionElement = dDoc.createElement("Persion");

    QDomElement Item1Element = dDoc.createElement("Item");
    Item1Element.setAttribute("Name", "Name");
    Item1Element.setAttribute("Type", "QString");
    QDomText Item1Text = dDoc.createTextNode("Item");
    Item1Text.setData(stPersion.sName);
    Item1Element.appendChild(Item1Text);
    PersionElement.appendChild(Item1Element);

    QDomElement Item2Element = dDoc.createElement("Item");
    Item2Element.setAttribute("Name", "Age");
    Item2Element.setAttribute("Type", "Int");
    QDomText Item2Text = dDoc.createTextNode("Item");
    Item2Text.setData(QString::number(stPersion.nAge));
    Item2Element.appendChild(Item2Text);
    PersionElement.appendChild(Item2Element);

    QDomElement Item3Element = dDoc.createElement("Item");
    Item3Element.setAttribute("Name", "Sex");
    Item3Element.setAttribute("Type", "QString");
    QDomText Item3Text = dDoc.createTextNode("Item");
    Item3Text.setData(stPersion.Sex);
    Item3Element.appendChild(Item3Text);
    PersionElement.appendChild(Item3Element);

    QDomElement Item4Element = dDoc.createElement("Item");
    Item4Element.setAttribute("Name", "Height");
    Item4Element.setAttribute("Type", "Int");
    QDomText Item4Text = dDoc.createTextNode("Item");
    Item4Text.setData(QString::number(stPersion.nHeight));
    Item4Element.appendChild(Item4Text);
    PersionElement.appendChild(Item4Element);

    QDomElement Item5Element = dDoc.createElement("Item");
    Item5Element.setAttribute("Name", "Weight");
    Item5Element.setAttribute("Type", "Int");
    QDomText Item5Text = dDoc.createTextNode("Item");
    Item5Text.setData(QString::number(stPersion.nWeight));
    Item5Element.appendChild(Item5Text);
    PersionElement.appendChild(Item5Element);

    sRootElement.appendChild(PersionElement);

    QFile file(XML_FILE_PATH);
    if(!file.open(QFile::WriteOnly | QFile::Text | QFile :: Truncate))//Truncate:覆盖重写
    {
        MY_DEBUG << "Open XML File Failed";
        return;
    }

    file.write(dDoc.toString().toLocal8Bit().data());
    file.close();
}

void CtXml::on_readFromXmlFileWithDom()
{
    QDomDocument dDoc;
    QFile file(XML_FILE_PATH);
    if(!file.open(QFile::ReadOnly | QFile::Text))
    {
        MY_DEBUG << "Open XML File Failed";
        return;
    }

    int iErrorLine;
    int iErrorCol;
    QString sErrorMsg;
    if (!dDoc.setContent(&file, false, &sErrorMsg, &iErrorLine, &iErrorCol))
    {
        sErrorMsg = QString::fromLocal8Bit("Parse Failed.");
        MY_DEBUG << "sErrorMsg:" << sErrorMsg;
        file.close();
        return;
    }
    file.close();

    PERSION_T stPersion;
    QDomElement deRootNode = dDoc.documentElement();
    if(deRootNode.tagName() == "Root")
    {
        MY_DEBUG << "readFromXmlFileWithDom 000";
        QDomNode PersionNode = deRootNode.firstChild();
        while(!PersionNode.isNull())
        {
            MY_DEBUG << "readFromXmlFileWithDom 111";
            QString sPersion = PersionNode.toElement().tagName();
            if(!sPersion.compare("Persion"))
            {
                MY_DEBUG << "readFromXmlFileWithDom 222";
                QDomNodeList ItemNodeList = PersionNode.childNodes();
                for(int i = 0; i < ItemNodeList.size(); i++)
                {
                    MY_DEBUG << "readFromXmlFileWithDom 333";
                    QDomElement ItemElement = ItemNodeList.at(i).toElement();
                    if(!ItemElement.tagName().compare("Item"))
                    {
                        MY_DEBUG << "readFromXmlFileWithDom 444";
                        if(ItemElement.hasAttribute("Name"))
                        {
                            MY_DEBUG << "readFromXmlFileWithDom 555";
                            QString str = ItemElement.attribute("Name");
                            if(!str.compare("Name"))
                            {
                                stPersion.sName = ItemElement.text();
                            }
                            else if(!str.compare("Age"))
                            {
                                stPersion.nAge = ItemElement.text().toInt();
                            }
                            else if(!str.compare("Sex"))
                            {
                                stPersion.Sex = ItemElement.text();
                            }
                            else if(!str.compare("Height"))
                            {
                                stPersion.nHeight = ItemElement.text().toInt();
                            }
                            else if(!str.compare("Weight"))
                            {
                                stPersion.nWeight = ItemElement.text().toInt();
                            }
                        }
                    }
                }
            }
            PersionNode = PersionNode.nextSibling();
        }
    }
    emit sig_returnXmlData(stPersion);
}

void CtXml::on_writeToXmlFileWithSax(PERSION_T stPersion)
{
    QFile file(XML_FILE_PATH2);
    if(!file.open(QFile::WriteOnly | QFile::Text | QFile :: Truncate))
    {
        MY_DEBUG << "XML File Open Failed.";
        return;
    }
    QXmlStreamWriter xmlWrite(&file);
    xmlWrite.setAutoFormatting(true);
    xmlWrite.writeStartDocument("1.0",true);
    xmlWrite.writeStartElement("Root");
    xmlWrite.writeStartElement("Persion");

    xmlWrite.writeStartElement ("Item");
    xmlWrite.writeAttribute("Name","Name");
    xmlWrite.writeAttribute("Type","QString");
    xmlWrite.writeCharacters(stPersion.sName);
    xmlWrite.writeEndElement();

    xmlWrite.writeStartElement ("Item");
    xmlWrite.writeAttribute("Name","Age");
    xmlWrite.writeAttribute("Type","Int");
    xmlWrite.writeCharacters(QString::number(stPersion.nAge));
    xmlWrite.writeEndElement();

    xmlWrite.writeStartElement ("Item");
    xmlWrite.writeAttribute("Name","Sex");
    xmlWrite.writeAttribute("Type","QString");
    xmlWrite.writeCharacters(stPersion.Sex);
    xmlWrite.writeEndElement();

    xmlWrite.writeStartElement ("Item");
    xmlWrite.writeAttribute("Name","Height");
    xmlWrite.writeAttribute("Type","Int");
    xmlWrite.writeCharacters(QString::number(stPersion.nHeight));
    xmlWrite.writeEndElement();

    xmlWrite.writeStartElement ("Item");
    xmlWrite.writeAttribute("Name","Weight");
    xmlWrite.writeAttribute("Type","Int");
    xmlWrite.writeCharacters(QString::number(stPersion.nWeight));
    xmlWrite.writeEndElement();

    xmlWrite.writeEndElement();    //Persion结束
    xmlWrite.writeEndElement();    //Root结束
    xmlWrite.writeEndDocument();   //Document结束

    file.close();
}

void CtXml::on_readFormXmlFileWithSax()
{
    QFile file(XML_FILE_PATH2);
    if(!file.open(QFile::ReadOnly | QFile::Text))
    {
        MY_DEBUG << "XML File Open Failed.";
        return;
    }

    PERSION_T stPersion;
    QXmlStreamReader xmlRead;
    xmlRead.setDevice(&file);
    if(xmlRead.readNextStartElement())
    {
        if(xmlRead.name().toString() == "Root")
        {
           if(xmlRead.readNextStartElement())
           {
               if(xmlRead.name().toString() == "Persion")
               {
                   while(xmlRead.readNextStartElement()) //循环读取Item
                   {
                      if(xmlRead.name().toString() == "Item")
                      {
                          QXmlStreamAttributes attributes = xmlRead.attributes();
                          if(attributes.hasAttribute("Name"))
                          {
                              QString sName = attributes.value("Name").toString();
                              if(!sName.compare("Name"))
                              {
                                  stPersion.sName = xmlRead.readElementText();
                              }
                              else if(!sName.compare("Age"))
                              {
                                  stPersion.nAge = xmlRead.readElementText().toInt();
                              }
                              else if(!sName.compare("Sex"))
                              {
                                  stPersion.Sex = xmlRead.readElementText();
                              }
                              else if(!sName.compare("Height"))
                              {
                                  stPersion.nHeight = xmlRead.readElementText().toInt();
                              }
                              else if(!sName.compare("Weight"))
                              {
                                  stPersion.nWeight = xmlRead.readElementText().toInt();
                              }
                              else
                              {
                                  xmlRead.readNextStartElement();
                              }
                          }
                      }
                   }
               }
           }
        }
    }
    emit sig_returnXmlData(stPersion);
}

mainwindow.h

#ifndef MAINWINDOW_H
#define MAINWINDOW_H

#include <QMainWindow>
#include "commondef.h"

namespace Ui {
class MainWindow;
}

class MainWindow : public QMainWindow
{
    Q_OBJECT

public:
    explicit MainWindow(QWidget *parent = nullptr);
    ~MainWindow();

signals:
    void sig_writeToXmlFileWithDom(PERSION_T stPersion);
    void sig_readFromXmlFileWithDom();
    void sig_writeToXmlFileWithSax(PERSION_T stPersion);
    void sig_readFormXmlFileWithSax();

public slots:
    void on_returnXmlData(PERSION_T stPersion);

private slots:
    void on_pushButton_Save_Dom_clicked();
    void on_pushButton_Read_Dom_clicked();
    void on_pushButton_Save_Sax_clicked();
    void on_pushButton_Read_Sax_clicked();

private:
    Ui::MainWindow *ui;
};

#endif // MAINWINDOW_

mainwindow.cpp

#include "mainwindow.h"
#include "ui_mainwindow.h"
#include "ctxml.h"
#include <QMessageBox>

MainWindow::MainWindow(QWidget *parent) :
    QMainWindow(parent),
    ui(new Ui::MainWindow)
{
    ui->setupUi(this);

    qRegisterMetaType<PERSION_T>("PERSION_T");
    connect(&CtXml::getInstance(), SIGNAL(sig_returnXmlData(PERSION_T)),
            this, SLOT(on_returnXmlData(PERSION_T)));
    connect(this, SIGNAL(sig_writeToXmlFileWithDom(PERSION_T)),
            &CtXml::getInstance(), SLOT(on_writeToXmlFileWithDom(PERSION_T)));
    connect(this, SIGNAL(sig_readFromXmlFileWithDom()),
            &CtXml::getInstance(), SLOT(on_readFromXmlFileWithDom()));
    connect(this, SIGNAL(sig_writeToXmlFileWithSax(PERSION_T)),
            &CtXml::getInstance(), SLOT(on_writeToXmlFileWithSax(PERSION_T)));
    connect(this, SIGNAL(sig_readFormXmlFileWithSax()),
            &CtXml::getInstance(), SLOT(on_readFormXmlFileWithSax()));
}

MainWindow::~MainWindow()
{
    delete ui;
}

void MainWindow::on_returnXmlData(PERSION_T stPersion)
{
    ui->lineEdit_Age->setText(QString::number(stPersion.nAge));
    ui->lineEdit_Sex->setText(stPersion.Sex);
    ui->lineEdit_Name->setText(stPersion.sName);
    ui->lineEdit_Height->setText(QString::number(stPersion.nHeight));
    ui->lineEdit_Weight->setText(QString::number(stPersion.nWeight));
}

void MainWindow::on_pushButton_Save_Dom_clicked()
{
    PERSION_T stPersion;
    stPersion.Sex = ui->lineEdit_Sex->text();
    stPersion.nAge = ui->lineEdit_Age->text().toInt();
    stPersion.sName = ui->lineEdit_Name->text();
    stPersion.nHeight = ui->lineEdit_Height->text().toInt();
    stPersion.nWeight = ui->lineEdit_Weight->text().toInt();

    emit sig_writeToXmlFileWithDom(stPersion);

    QMessageBox::information(this, "Tip", "Save Xml With Dom Success.");
}

void MainWindow::on_pushButton_Read_Dom_clicked()
{
    emit sig_readFromXmlFileWithDom();
}

void MainWindow::on_pushButton_Save_Sax_clicked()
{
    PERSION_T stPersion;
    stPersion.Sex = ui->lineEdit_Sex->text();
    stPersion.nAge = ui->lineEdit_Age->text().toInt();
    stPersion.sName = ui->lineEdit_Name->text();
    stPersion.nHeight = ui->lineEdit_Height->text().toInt();
    stPersion.nWeight = ui->lineEdit_Weight->text().toInt();

    emit sig_writeToXmlFileWithSax(stPersion);

    QMessageBox::information(this, "Tip", "Save Xml With Sax Success.");
}

void MainWindow::on_pushButton_Read_Sax_clicked()
{
    emit sig_readFormXmlFileWithSax();
}

3)demo工程下载

下载地址链接:https://download.csdn.net/download/linyibin_123/87374629

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/150075.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

纵向联邦线性回归实现-Federated Machine Learning Concept and Applications论文复现

本实验的算法实现思路来自这篇论文Federated Machine Learning Concept and Applications 文章目录场景介绍同态加密算法python的phe库实现了加法同态加密角色1角色2传统的线性回归纵向联邦线性回归纵向联邦线性回归代码实现导入工具包准备数据使用普通线性回归训练搭建训练过程…

什么神仙操作,用代码能画这样的图

大家好&#xff0c;我是车辙。不知道同学们画流程图或者时序图一般用的什么软件&#xff1f;Visio 还是 Process On 或者语雀&#xff1f; 因为公司原因&#xff0c;在很多情况下&#xff0c;我一般用语雀画流程图或者思维导图。不过凡事也有例外&#xff0c;对于比较简单的图…

你的电路是抄来的还是算出来的?

在你看这篇文章之前&#xff0c;我想提出几点说明&#xff1a; &#xff08;1&#xff09;最近在看拉扎维的书&#xff0c;写下来这些东西&#xff0c;这也只是我个人在学习过程中的一点总结&#xff0c;有什么观点大家可以相互交流&#xff1b;&#xff08;2&#xff09;不断的…

立创eda专业版学习笔记(3)(隐藏部分飞线)

又到了喜闻乐见的隐藏gnd飞线环节&#xff0c;我发现这个专业版的操作和标志版不一样&#xff0c;我想试一试这个标题的搜索结果&#xff0c;发现有用的结果还是很少&#xff0c;于是我也随便总结了一下&#xff0c;算是添砖加瓦吧。 原来的飞线是这个样子的&#xff1a; 现在我…

巧妙解决appleid问题答案忘了的问题

先说下这个问题解决办法的目标——主要是为了释放被占用的appleid邮箱&#xff0c;而如果你想保留该appleid并且正常使用的话&#xff0c;那么需要付出一点代价&#xff0c;也是可以做到的。 我最近就碰到这种情况&#xff0c;某个邮箱被appleid占用了&#xff0c;问题答案因为…

从实战出发,聊聊缓存数据库一致性

在云服务中&#xff0c;缓存是极其重要的一点。所谓缓存&#xff0c;其实是一个高速数据存储层。当缓存存在后&#xff0c;日后再次请求该数据就会直接访问缓存&#xff0c;提升数据访问的速度。但是缓存存储的数据通常是短暂性的&#xff0c;这就需要经常对缓存进行更新。而我…

Linux常用命令——lsb_release命令

在线Linux命令查询工具 lsb_release 显示发行版本信息 补充说明 LSB是Linux Standard Base的缩写&#xff0c;lsb_release命令用来显示LSB和特定版本的相关信息。如果使用该命令时不带参数&#xff0c;则默认加上-v参数。 -v 显示版本信息。 -i 显示发行版的id。 -d 显示该…

2023 Real World CTF 体验赛 --- wp

文章目录misc&#x1f411;了拼&#x1f411;webEvil MySQL ServerBe-a-Language-ExpertBe-a-Wiki-HackerYummy ApiApacheCommandTextmisc &#x1f411;了拼&#x1f411; 游戏类题目&#xff0c;直接打开js文件搜索rwctf&#xff0c;发现flag rwctf{wellcome_to_the_rwct…

跟着开源项目学java8-从支持最大密码重试次数的提交看redis的场景化使用和基于jdk的schedule的异步延迟日志记录策略

我们这里要实现的功能是登录时添加账号登录错误时最大错误次数和锁定时间&#xff0c;功能不复杂&#xff0c;这次提交里面我们主要来看下一个项目里面一个业务功能怎样写更加优雅 核心实现 我们先来看核心实现的思路 首先是 login 方法重写&#xff0c;进入 loadUserByUser…

【vue2】Vue Cli脚手架与VueTools的安装详解

&#x1f973;博 主&#xff1a;初映CY的前说(前端领域) &#x1f31e;个人信条&#xff1a;想要变成得到&#xff0c;中间还有做到&#xff01; &#x1f918;本文核心&#xff1a;Vue Cli脚手架与VueTools的安装详解 目录 一、vue-cli脚手架工具的安装及文件介绍 1.v…

ArcGIS基础实验操作100例--实验72土地利用变化分析

本实验专栏参考自汤国安教授《地理信息系统基础实验操作100例》一书 实验平台&#xff1a;ArcGIS 10.6 实验数据&#xff1a;请访问实验1&#xff08;传送门&#xff09; 高级编辑篇--实验72 土地利用变化分析 目录 一、实验背景 二、实验数据 三、实验步骤 &#xff08;1&…

ArcGIS 制图流程 非常详细

如何在ArcMap中从头到尾制作一幅专题图&#xff1f;你可以看本编教程&#xff0c;从准备数据到最终导出成图&#xff0c;一步一步进行操作&#xff0c;一定可以教会你。 至于为何使用英文版软件&#xff0c;你如果作图就会知道一般经纬度都是利用英文显示&#xff0c;不然最终…

微信小程序实现左边图片右边文字效果

实现的效果&#xff1a; xml布局文件&#xff1a; <view class"chuxingItem"> <image class"img" src"/pages/image/banche.png"></image> <view style"font-size: 30rpx;margin-left: 15rpx;">班车查询</…

Leetcode:513. 找树左下角的值(C++)

目录 问题描述&#xff1a; 实现代码与解析&#xff1a; 递归&#xff1a; 原理思路&#xff1a; 层序遍历&#xff1a; 原理思路&#xff1a; 问题描述&#xff1a; 给定一个二叉树的 根节点 root&#xff0c;请找出该二叉树的 最底层 最左边 节点的值。 假设二叉树中至…

kaggle平台学习复习笔记 | Data Visualization | Seaborn

目录1.hello Seaborn2.Line Charts3.Bar Charts and Heatmaps4.Scatter PlotsDistributions5.Choosing Plot Types and Custom Styles1.hello Seaborn import pandas as pd pd.plotting.register_matplotlib_converters() import matplotlib.pyplot as plt %matplotlib inline…

阿里云服务器安装mysql数据库教程

阿里云服务器怎么安装mysql数据库&#xff1f;阿里云服务器ECS如何安装mysql数据库教程。主机教程网下面就来分享一下阿里云服务器安装mysql数据库教程。 第一步 1、登录个人的阿里云服务管理终端 2、点击进入远程连接&#xff0c;输入之前设置的远程登录密码&#xff08;如…

能够激发创作灵感的笔记软件,强大在哪里? #RoamResearch

今天的人类知识体系&#xff0c;已经汇聚成了一个浩瀚的信息与思想的海洋&#xff0c;信息量呈指数级增长&#xff0c;如果能够解决潜在的协作问题&#xff0c;这会给个体带来巨大的机会。怎么有效利用信息&#xff1f;如何搭建自己的知识体系&#xff1f;这些都是信息爆炸的时…

在ubuntu系统上用pyinstaller打包yolov5项目代码

目录0. 背景1. 创建虚拟环境2. pyinstaller打包2.1. 生成并修改spec文件2.2. 重新生成二进制文件3. 测试0. 背景 最近需要在ubuntu 18.04上将自己写的一些基于yolov5的项目代码打包成二进制文件&#xff0c;方便部署的同时也尽量减少暴露源码。 参考网上的很多教程&#xff0…

Node.JS(4)--模块、exports和module

文章目录模块核心模块文件模块基本数据类型引用数据类型exports和module.exports的关系模块 分为两大类 核心模块 由node引擎提供的模块 核心模块的标识就是模块的名字 var fsrequire("fs");文件模块 由用户自己创建的模块文件模块的标识就是文件的路径&#x…

一个专注推荐.Net开源项目的榜单

大家好&#xff0c;我是编程乐趣&#xff0c;从7月份开始推荐开源项目&#xff0c;已经推荐了接近100个开源项目了&#xff0c;其中绝大部分是有关.Net的开源项目&#xff0c;也受到大家非常多人的喜欢。 由于公众号不方便查询&#xff0c;很多人又想了解更多的开源项目&#…