`
ajuanlong
  • 浏览: 108074 次
社区版块
存档分类
最新评论

DOM、JDOM、DOM4J解析XML实例

阅读更多

一、DOM方式

原始name.xml

Html代码 
  1. <?xml version="1.0" encoding="GB2312" standalone="no"?><学生花名册>  
  2.     <学生 性别="男">  
  3.         <姓名>李华</姓名>  
  4.         <年龄>14</年龄>  
  5.     </学生>  
  6.     <学生 性别="男">  
  7.         <姓名>张三</姓名>  
  8.         <年龄>16</年龄>  
  9.     </学生>  
  10.     <学生 性别="女">  
  11.         <姓名>王娟</姓名>  
  12.         <年龄>18</年龄>  
  13.     </学生>  
  14. </学生花名册>  

数据模型Student.java

Java代码 
  1. package com.upcgrid.dom;  
  2.   
  3. public class Student {  
  4.     private String name;  
  5.     private String sex;  
  6.     private int age;  
  7.   
  8.     public String getName() {  
  9.         return name;  
  10.     }  
  11.   
  12.     public void setName(String name) {  
  13.         this.name = name;  
  14.     }  
  15.   
  16.     public String getSex() {  
  17.         return sex;  
  18.     }  
  19.   
  20.     public void setSex(String sex) {  
  21.         this.sex = sex;  
  22.     }  
  23.   
  24.     public int getAge() {  
  25.         return age;  
  26.     }  
  27.   
  28.     public void setAge(int age) {  
  29.         this.age = age;  
  30.     }  
  31. }  

DOM方式解析xml文档示例程序

Java代码 
  1. package com.upcgrid.dom;  
  2.   
  3. import java.io.FileOutputStream;  
  4. import java.io.IOException;  
  5. import java.io.OutputStreamWriter;  
  6. import java.util.Vector;  
  7.   
  8. import javax.xml.parsers.DocumentBuilder;  
  9. import javax.xml.parsers.DocumentBuilderFactory;  
  10. import javax.xml.parsers.ParserConfigurationException;  
  11. import javax.xml.transform.OutputKeys;  
  12. import javax.xml.transform.Transformer;  
  13. import javax.xml.transform.TransformerException;  
  14. import javax.xml.transform.TransformerFactory;  
  15. import javax.xml.transform.dom.DOMSource;  
  16. import javax.xml.transform.stream.StreamResult;  
  17.   
  18. import org.w3c.dom.Document;  
  19. import org.w3c.dom.Element;  
  20. import org.w3c.dom.NodeList;  
  21. import org.w3c.dom.Text;  
  22. import org.xml.sax.SAXException;  
  23.   
  24. /** 
  25.  *  
  26.  * @author shijin 原始DOM解析XML方式 
  27.  *  
  28.  */  
  29. public class DOMTest {  
  30.     public static void main(String[] args) throws ParserConfigurationException,  
  31.             SAXException, IOException, TransformerException {  
  32.         DOMTest test = new DOMTest();  
  33.         test.deleteElement("name.xml""女");  
  34.         Vector<Student> stus = test.changeXMLToModel("name.xml");  
  35.         for (Student stu : stus) {  
  36.             System.out.print(stu.getName() + " " + stu.getSex() + " "  
  37.                     + stu.getAge() + "\n");  
  38.         }  
  39.         // 输出:  
  40.         // 李华 男 14  
  41.         // 张三 男 16  
  42.         test.createDocument("name2.xml", stus);  
  43.     }  
  44.   
  45.     /** 
  46.      * show 根据vector中的student对象创建xml文档 
  47.      *  
  48.      * @param filename 
  49.      *            要创建的文档名 
  50.      * @throws ParserConfigurationException 
  51.      * @throws SAXException 
  52.      * @throws IOException 
  53.      * @throws TransformerException 
  54.      */  
  55.     public void createDocument(String filename, Vector<Student> stus)  
  56.             throws ParserConfigurationException, SAXException, IOException,  
  57.             TransformerException {  
  58.         // 得到DOM解析器的工厂实例  
  59.         DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();  
  60.         // 从DOM工厂获取DOM解析器  
  61.         DocumentBuilder builder = dbf.newDocumentBuilder();  
  62.         // 创建document对象  
  63.         Document doc = builder.newDocument();  
  64.         Element root = doc.createElement("学生花名册");// 创建根元素  
  65.         doc.appendChild(root);// 添加根元素  
  66.         for (Student stu : stus) {  
  67.             Element stuElement = doc.createElement("学生");  
  68.             stuElement.setAttribute("性别", stu.getSex());  
  69.             Element nameElement = doc.createElement("姓名");  
  70.             Text nameText = doc.createTextNode(stu.getName());  
  71.             nameElement.appendChild(nameText);  
  72.             Element ageElement = doc.createElement("年龄");  
  73.             Text ageText = doc.createTextNode(String.valueOf(stu.getAge()));  
  74.             ageElement.appendChild(ageText);  
  75.             stuElement.appendChild(nameElement);  
  76.             stuElement.appendChild(ageElement);  
  77.             root.appendChild(stuElement);  
  78.         }  
  79.         FileOutputStream fos = new FileOutputStream(filename);  
  80.         OutputStreamWriter ow = new OutputStreamWriter(fos);  
  81.         // 保存xml文件  
  82.         DOMSource doms = new DOMSource(doc);  
  83.         // 创建结果输出流  
  84.         StreamResult result = new StreamResult(ow);  
  85.         // 得到转换工厂实例  
  86.         TransformerFactory transFac = TransformerFactory.newInstance();  
  87.         // 得到转换器实例  
  88.         Transformer trans = transFac.newTransformer();  
  89.         // 转化器设置输出文档的编码方式  
  90.         trans.setOutputProperty(OutputKeys.ENCODING, "GB2312");  
  91.         //设置支持缩进  
  92.         trans.setOutputProperty(OutputKeys.INDENT, "yes");  
  93.         // 将文档源转换到结果输出流  
  94.         trans.transform(doms, result);  
  95.     }  
  96.   
  97.     /** 
  98.      * show 读取指定XML文档并将其对象化 
  99.      *  
  100.      * @param filename 
  101.      * @return 存储Student对象的vector 
  102.      * @throws ParserConfigurationException 
  103.      * @throws SAXException 
  104.      * @throws IOException 
  105.      */  
  106.     public Vector<Student> changeXMLToModel(String filename)  
  107.             throws ParserConfigurationException, SAXException, IOException {  
  108.         // 得到DOM解析器的工厂实例  
  109.         DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();  
  110.         // 从DOM工厂获取DOM解析器  
  111.         DocumentBuilder builder = dbf.newDocumentBuilder();  
  112.         // 解析xml文档,获得document对象,即DOM树  
  113.         Document doc = builder.parse(filename);  
  114.         // 获取根节点  
  115.         Element root = doc.getDocumentElement();  
  116.         // 获取根节点的子节点中名字为"学生"的节点列表  
  117.         NodeList stuNodes = root.getElementsByTagName("学生");  
  118.         Vector<Student> students = new Vector<Student>();  
  119.         // 遍历<学生>节点  
  120.         for (int i = 0; i < stuNodes.getLength(); i++) {  
  121.             // 获取一个学生节点  
  122.             Element stuNode = (Element) stuNodes.item(i);  
  123.             // 创建一个学生对象  
  124.             Student stu = new Student();  
  125.             stu.setSex(stuNode.getAttribute("性别"));  
  126.             // 获取<学生>节点的子节点中名字为"姓名"的节点列表  
  127.             NodeList nameNodes = stuNode.getElementsByTagName("姓名");  
  128.             // 取得第一个姓名  
  129.             Element nameNode = (Element) nameNodes.item(0);  
  130.             stu.setName(nameNode.getTextContent());  
  131.             // 获取<学生>节点的子节点中名字为"年龄"的节点列表  
  132.             NodeList ageNodes = stuNode.getElementsByTagName("年龄");  
  133.             // 取得第一个年龄  
  134.             Element ageNode = (Element) ageNodes.item(0);  
  135.             stu.setAge(Integer.parseInt(ageNode.getTextContent()));  
  136.             students.add(stu);  
  137.         }  
  138.         return students;  
  139.     }  
  140.   
  141.     /** 
  142.      * show 删除指定名为filename的xml文档中学生结点的性别属性为tagname的结点 
  143.      *  
  144.      * @param filename 
  145.      * @param tagname 
  146.      * @throws ParserConfigurationException 
  147.      * @throws SAXException 
  148.      * @throws IOException 
  149.      * @throws TransformerException 
  150.      */  
  151.     public void deleteElement(String filename, String tagname)  
  152.             throws ParserConfigurationException, SAXException, IOException,  
  153.             TransformerException {  
  154.         // 得到DOM解析器的工厂实例  
  155.         DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();  
  156.         // 从DOM工厂获取DOM解析器  
  157.         DocumentBuilder builder = dbf.newDocumentBuilder();  
  158.         // 解析xml文档,获得document对象,即DOM树  
  159.         Document doc = builder.parse(filename);  
  160.         // 找到删除节点  
  161.         NodeList stuNodes = doc.getElementsByTagName("学生");  
  162.         for (int i = 0; i < stuNodes.getLength(); i++) {  
  163.             Element stuElement = (Element) stuNodes.item(i);  
  164.             String sex = stuElement.getAttribute("性别");  
  165.             if (sex.equals(tagname)) {  
  166.                 stuElement.getParentNode().removeChild(stuElement);  
  167.             }  
  168.         }  
  169.         // 保存xml文件  
  170.         DOMSource doms = new DOMSource(doc);  
  171.         // 创建结果输出流  
  172.         StreamResult result = new StreamResult(new FileOutputStream(filename));  
  173.         // 得到转换工厂实例  
  174.         TransformerFactory transFac = TransformerFactory.newInstance();  
  175.         // 得到转换器实例  
  176.         Transformer trans = transFac.newTransformer();  
  177.         // 转化器设置输出文档的编码方式  
  178.         trans.setOutputProperty(OutputKeys.ENCODING, "GB2312");  
  179.         // 将文档源转换到结果输出流  
  180.         trans.transform(doms, result);  
  181.     }  
  182. }  

输出:
李华 男 14
张三 男 16
结果name.xml

Java代码 
  1. <?xml version="1.0" encoding="GB2312" standalone="no"?><学生花名册>  
  2.     <学生 性别="男">  
  3.         <姓名>李华</姓名>  
  4.         <年龄>14</年龄>  
  5.     </学生>  
  6.     <学生 性别="男">  
  7.         <姓名>张三</姓名>  
  8.         <年龄>16</年龄>  
  9.     </学生>  
  10.       
  11. </学生花名册>  

name2.xml

Java代码 
  1. <?xml version="1.0" encoding="GB2312" standalone="no"?>  
  2. <学生花名册>  
  3. <学生 性别="男">  
  4. <姓名>李华</姓名>  
  5. <年龄>14</年龄>  
  6. </学生>  
  7. <学生 性别="男">  
  8. <姓名>张三</姓名>  
  9. <年龄>16</年龄>  
  10. </学生>  
  11. </学生花名册>  
二、JDOM方式

HD.xml

Html代码 
  1. <?xml version="1.0" encoding="UTF-8"?>  
  2. <HD>  
  3.   <disk name="C">  
  4.     <capacity>8G</capacity>  
  5.     <directories>200</directories>  
  6.     <files>1580</files>  
  7.   </disk>  
  8.   
  9.   <disk name="D">  
  10.     <capacity>10G</capacity>  
  11.     <directories>500</directories>  
  12.     <files>3000</files>  
  13.   </disk>  
  14. </HD>   

JDOM解析XML文档示例

Html代码 
  1. package com.upcgrid.jdom;  
  2.   
  3. import java.io.IOException;  
  4. import java.util.List;  
  5. import org.jdom.Document;  
  6. import org.jdom.Element;  
  7. import org.jdom.JDOMException;  
  8. import org.jdom.input.SAXBuilder;  
  9.   
  10. public class JDOMTest {  
  11.     public static void main(String[] args) throws JDOMException, IOException {  
  12.         // 获得JDOM以SAX方式处理XML文档的构造器  
  13.         SAXBuilder saxb = new SAXBuilder();  
  14.         // 通过SAX构造器获得XML文档对象  
  15.         Document doc = saxb.build(JDOMTest.class.getClassLoader()  
  16.                 .getResourceAsStream("test.xml"));  
  17.         // 获取根元素HD  
  18.         Element root = doc.getRootElement();  
  19.         // 取名字为disk的所有元素  
  20.         List<Element> list = root.getChildren("disk");  
  21.         for (int i = 0; i < list.size(); i++) {  
  22.             //取得第i个disk节点  
  23.             Element diskElement = (Element) list.get(i);  
  24.             //取得disk属性name  
  25.             String name = diskElement.getAttributeValue("name");  
  26.             // 取disk子元素capacity的内容  
  27.             String capacity = diskElement.getChildText("capacity");  
  28.             String directories = diskElement.getChildText("directories");  
  29.             String files = diskElement.getChildText("files");  
  30.             System.out.println("磁盘信息:");  
  31.             System.out.println("分区盘符:" + name);  
  32.             System.out.println("分区容量:" + capacity);  
  33.             System.out.println("目录数:" + directories);  
  34.             System.out.println("文件数:" + files);  
  35.             System.out.println("-----------------------------------");  
  36.         }  
  37.     }  
  38. }  

输出结果:
磁盘信息:
分区盘符:C
分区容量:8G
目录数:200
文件数:1580
-----------------------------------
磁盘信息:
分区盘符:D
分区容量:10G
目录数:500
文件数:3000
-----------------------------------

三、DOM4J方式

User.hbm.xml

Html代码 
  1. <?xml version="1.0" encoding="UTF-8"?>  
  2. <hibernate-mapping>  
  3.     <class name="com.upcgrid.User" table="t_user">  
  4.         <property name="username">aaa</property>  
  5.         <property name="password">bbb</property>  
  6.     </class>>  
  7. </hibernate-mapping>  

DOM4J解析XML示例

Java代码 
  1. package com.upcgrid.dom4j;  
  2.   
  3. import java.io.File;  
  4. import java.io.FileWriter;  
  5. import java.io.IOException;  
  6. import java.util.Iterator;  
  7. import java.util.List;  
  8.   
  9. import org.dom4j.Attribute;  
  10. import org.dom4j.Document;  
  11. import org.dom4j.DocumentException;  
  12. import org.dom4j.DocumentHelper;  
  13. import org.dom4j.Element;  
  14. import org.dom4j.Node;  
  15. import org.dom4j.io.OutputFormat;  
  16. import org.dom4j.io.SAXReader;  
  17. import org.dom4j.io.XMLWriter;  
  18.   
  19. public class DOM4JTest {  
  20.     public static void main(String[] args) {  
  21.         //获得DOM4J以SAX方式处理XML文档的管道  
  22.         SAXReader reader = new SAXReader();  
  23.         try {  
  24.             //通过管道获得XML文档对象  
  25.             Document document = reader.read(new File("User.hbm.xml"));  
  26.             //获取根节点  
  27.             Element root = document.getRootElement();  
  28.             System.out.println(root.getName());  
  29.               
  30.             Element element = null;  
  31.             //遍历根节点下的节点  
  32.             for(Iterator<Element> i = root.elementIterator();i.hasNext();){  
  33.                 //根节点的子节点  
  34.                 element = (Element) i.next();  
  35.                 System.out.println(element.getName());  
  36.                 //遍历节点属性  
  37.                 for(Iterator<Attribute> j = element.attributeIterator();j.hasNext();){  
  38.                     Attribute attr = (Attribute)j.next();  
  39.                     System.out.println(attr.getName()+":"+attr.getValue());  
  40.                 }  
  41.                 //遍历名为"property"的节点  
  42.                 for(Iterator<Element> k = element.elementIterator("property");k.hasNext();){  
  43.                     Element e1 = (Element)k.next();  
  44.                     System.out.println(e1.getData());  
  45.                 }  
  46.             }  
  47.               
  48.             //通过Xpath的方式寻找节点  
  49.             List<Node> list = document.selectNodes("//hibernate-mapping/class/property");  
  50.             for(Node n: list){  
  51.                 String name = n.valueOf("@name");  
  52.                 System.out.println(n.getName()+" @name:"+name);  
  53.             }  
  54.             //只拿第一个  
  55.             Node node = document.selectSingleNode("//hibernate-mapping/class");  
  56.             String name = node.valueOf("@name");  
  57.             String table = node.valueOf("@table");  
  58.             System.out.println(node.getName()+" @name:"+name+",@table:"+table);  
  59.             createDocument();  
  60.         } catch (DocumentException e) {  
  61.             e.printStackTrace();  
  62.         }  
  63.     }  
  64.       
  65.     public static Document createDocument(){  
  66.         //创建document对象  
  67.         Document document = DocumentHelper.createDocument();  
  68.         Element root = document.addElement("hibernate-mapping");  
  69.         //链式编程  
  70.         Element classElement = root.addElement("class")  
  71.             .addAttribute("name""com.upcgrid.User")  
  72.             .addAttribute("table""t_user");  
  73.         classElement.addElement("property").addAttribute("name""username").addText("aaa");  
  74.         classElement.addElement("Property").addAttribute("name""password").addText("bbb");;  
  75.         try {  
  76.             FileWriter out = new FileWriter("User1.hbm.xml");  
  77.             document.write(out);  
  78.             out.flush();  
  79.             out.close();  
  80.         } catch (IOException e) {  
  81.             e.printStackTrace();  
  82.         }  
  83.         try {  
  84.             //设定美化格式  
  85.             OutputFormat format = OutputFormat.createPrettyPrint();  
  86.             XMLWriter writer = new XMLWriter(new FileWriter("User2.hbm.xml"),format);  
  87.             writer.write(document);  
  88.             writer.close();  
  89.             //缩减格式  
  90.         } catch (IOException e) {  
  91.             e.printStackTrace();  
  92.         }  
  93.         return document;  
  94.     }  
  95. }  

输出:
hibernate-mapping
class
name:com.upcgrid.User
table:t_user
aaa
bbb
property @name:username
property @name:password
class @name:com.upcgrid.User,@table:t_user
User.hbm.xml

Html代码 
  1. <?xml version="1.0" encoding="UTF-8"?>  
  2. <hibernate-mapping><class name="com.upcgrid.User" table="t_user"><property name="username">aaa</property><Property name="password">bbb</Property></class></hibernate-mapping>  

User2.hbm.xml

Html代码 
  1. <?xml version="1.0" encoding="UTF-8"?>  
  2.   
  3. <hibernate-mapping>  
  4.   <class name="com.upcgrid.User" table="t_user">  
  5.     <property name="username">aaa</property>  
  6.     <Property name="password">bbb</Property>  
  7.   </class>  
  8. </hibernate-mapping>
17
3
分享到:
评论
3 楼 enen1982 2012-06-21  
dom4j当然强大,支付xquery表达式和xpath寻止,方便多了
2 楼 hyj0903 2012-06-21  
性能方面呢?比如说用这此方法生成一棵树,XML文件大小2M.
1 楼 dyllove98 2012-06-20  
整理的不错

相关推荐

    DOM、JDOM、DOM4J解析XML实例.pdf

    ### DOM、JDOM、DOM4J解析XML实例详解 #### DOM 方式解析 XML 实例解析 ##### 一、DOM 概述与应用实例 DOM(Document Object Model)是一种树状结构的标准,用于表示 XML 文档的数据结构。它将 XML 文档视为节点...

    用DOM SAX JDom dom4j 对xml文档解析实例

    本文将深入探讨DOM、SAX、JDom和dom4j四种流行的Java XML解析器,以及它们在处理XML文档时的特点和用法。 1. DOM(Document Object Model) DOM是W3C推荐的一种解析XML的标准方法。它将XML文档表示为一个树形结构,...

    \"java xml 四\"之JDOM、DOM4J解析XML总结

    在压缩包中的“xml_6.pdf”文件可能包含了更详细的JDOM和DOM4J解析XML的教程,包括代码示例和最佳实践,建议进一步阅读以加深理解。 总的来说,JDOM和DOM4J都是强大的XML处理工具,选择哪个取决于具体项目的需求。...

    DOM JDOM dom4j 实例源码

    以下是一些基于DOM、JDOM和dom4j的实例源码知识点: 1. **DOM实例**: - 加载XML文档:使用`DocumentBuilderFactory`创建`DocumentBuilder`,再通过`parse()`方法解析XML文件。 - 访问节点:`...

    Jdom、Dom4j 、dom解析xml文件

    在Java中,解析XML文件是常见的任务,有多种库供开发者选择,其中包括JDOM、DOM4J以及DOM。这三种解析器各有特点,下面将详细解释它们的工作原理和使用方法。 首先,我们来看看DOM(Document Object Model)解析器...

    dom4J生成xml、解析xml、生成xml文件实例

    DOM4J是一个强大的Java XML API,它不仅提供了DOM、SAX和JDOM等接口的实现,还提供了一种更简单、更灵活的方式来处理XML文档。这篇博客()可能详细介绍了如何使用DOM4J来生成和解析XML文件,这对于处理XML文档的...

    dom4j解析xml

    【DOM4J解析XML】 DOM4J是一个Java库,它提供了强大的XML处理功能,包括解析、操作和生成XML文档。DOM4J相比W3C的DOM API,具有更轻量级、性能更好以及更易于使用的特性。在Java中,解析XML文档主要有两种方式:SAX...

    dom4j解析XML所需要的包

    下面我们将深入探讨DOM4J解析XML的基本概念、使用方法以及它与其他XML解析器的比较。 XML(eXtensible Markup Language)是一种标记语言,广泛应用于数据交换、配置文件和文档结构化存储。DOM4J是基于DOM(Document...

    dom4j-1.6.1-解析xml架包.zip

    在实际使用DOM4J解析XML时,我们通常遵循以下步骤: 1. **解析XML**: 使用`DocumentFactory`创建一个`DocumentBuilder`实例,然后用它来解析XML文件,得到`Document`对象。 ```java SAXReader reader = new ...

    java四种xml解析实例和jar包,包括dom,sax,dom4j,jdom等方式

    java四种xml解析实例和jar包,包括dom,sax,dom4j,jdom等方式 java四种xml解析实例和jar包,包括dom,sax,dom4j,jdom等方式 是学习Java的xml解析的好例子,包括相应的dom4j.jar,jdom.jar等,自己整理的,物超所值啊,...

    jdom+dom4j

    使用DOM4J解析XML的典型流程如下: 1. 创建`DocumentFactory`实例,它是DOM4J的工厂类。 2. 使用`DocumentFactory`的`parse()`方法解析XML文件,得到`Document`对象。 3. 利用`Document`对象,可以访问XML的元素、...

    dom4j和jdom解析XML小例子

    下面是一段使用DOM4J解析XML的代码示例: ```java import org.dom4j.Document; import org.dom4j.Element; import org.dom4j.io.SAXReader; // 创建一个SAXReader实例 SAXReader saxReader = new SAXReader(); // ...

    Dom4j解析和生成XML文档

    DOM4J解析XML文档: DOM4J通过构建DOM树(Document Object Model)来解析XML。DOM模型是一种将XML文档转换为内存中对象树的表示方式。在DOM4J中,我们可以使用`DocumentBuilderFactory`和`DocumentBuilder`来创建`...

    jdom4j解析xml

    ### jdom4j解析XML实例详解 #### 生成XML文档 在示例代码中,`generateXML`方法用于创建一个XML文档。首先,通过`DocumentHelper.createDocument()`调用创建了一个空白的`Document`对象,这相当于创建了一个XML文件...

    JDOM使用详解及实例(解析xml

    JDOM是一个专为Java设计的XML处理库,它结合了SAX和DOM的优点,提供了一种更高效且方便的方式来解析、创建和操作XML文档。JDOM的核心思想是创建一个基于Java对象模型的XML文档表示,使得开发者能够利用Java的强大...

    JAVA 解析XML生成XML文档实例

    JAVA 解析XML和生成XML文档源码。比较全 1.DOM生成和解析XML文档 2.SAX生成和解析XML文档 3.DOM4J生成和解析XML文档 4.JDOM生成和解析XML

    JDOM解析xml文档实例(自己编写,仅供参考)

    本实例将深入讲解如何使用JDOM进行XML解析。 首先,我们需要在项目中引入JDOM的类库。JDOM官方网站提供了不同版本的JDOM jar包,你可以根据项目需求选择合适的版本下载。下载后,可以通过以下几种方式将其添加到...

    java jdom解析xml

    - **SAX与DOM对比**:JDOM采用的是DOM模型,相比SAX解析器,它会将整个XML文档加载到内存中,适合小型XML文件。对于大型文件,可以考虑使用SAX或StAX以减少内存消耗。 - **优化策略**:如果确实需要处理大型XML...

Global site tag (gtag.js) - Google Analytics