【问题标题】:How to read an XML file and validate with the data from a table如何读取 XML 文件并使用表中的数据进行验证
【发布时间】:2021-08-09 23:14:12
【问题描述】:

我正在创建一个允许加载 XML 文件的功能,但在加载它们之前我需要验证它们的内容,我不知道这是否是正确的方法,但我正在考虑类似首先阅读 XML 以查看它的内容,然后使用我存储在数据库表中的数据对其进行验证。

目前我开发的是使用 AJAX 和 PHP 加载 XML 文件,如下所示。

这是我用来加载 XML 文件的表单的一部分

<form action="" enctype="multipart/form-data">
          <div class="form-group row">
                        <label for="fileToUpload" class="col-sm-3 col-form-label"> File XML:</label>
              <div class="col-sm-8">
                <input type="file" name="fileToUpload" id="XmlToUpload" class="btn" accept=".xml" required>
              </div>
                    </div>
                    

          <div class="modal-footer">
            <button type="button" class="btn btn-secondary" data-dismiss="modal">Cancel</button>
            <button type="button" id="upload" class="btn btn-success">Upload Documents</button>
          </div>
      </form>

以下是我用来加载文档并验证其格式的 JQuery 函数

$(document).ready(function(){
    $("#upload").on("click", function(){
        const ArchivoXml = $("#XmlToUpload").prop('files')[0];          

        
        if (ArchivoXml.type !== 'text/xml') {
            $('#XmlToUpload').val('');
            //$('#pdfToUpload').val('');
            alert('Please select the files with the correct format');
            return;
          }

        const datosForm = new FormData;

        datosForm.append("XmlToUpload", ArchivoXml);
       
        const filePath="server.php"
        //console.log(ArchivoXml);
        $.ajax({
            type:'POST',
            cache: false,
            contentType: false,
            processData: false,
            data: datosForm,
            url:filePath
        }).done(function(data){
            alert(data)
        }).fail(function(){
            alert('The file could not be loaded')
        });       
    });   
});

以下是我指明存储路径的 PHP 代码

server.php

<?php
    if(isset($_FILES["XmlToUpload"])){
        $archivo = $_FILES["XmlToUpload"]["name"];
        $ruta ="XML/";

        if(move_uploaded_file($_FILES["XmlToUpload"]["tmp_name"],$ruta.$archivo)){
            echo "Successfully uploaded";
        }else{
            echo "Error";
        }          
    }else {
        echo "A file was not selected";
    }
?>

正如我在问题开头提到的,我需要验证 XML 的内容,并使用在数据库表中找到的信息进行验证。

XML 文件的结构将包含以下信息:

<?xml version="1.0" encoding="UTF-8"?>
<cfdi:Voucher Version="3.3" Total="45264.13">
    <cfdi:Issuer Rfc="ABC123456AB1" Name="JOHN DOE"/>
     <cfdi:Concepts>
         <cfdi:Concept UnitValue="93.80">
        </cfdi:Concept>
    </cfdi:Concepts>
     <cfdi:Plugin>
        <tfd:Digital Version="1.1" />
    </cfdi:Plugin>
</cfdi:Voucher>

我有兴趣验证的数据是 Total="45264.13"Rfc="ABC123456AB1"

除此之外,我开始了在 SQLServer 中创建存储过程的任务,以从数据库中获取信息,从而与 XML 数据和数据库中表的字段值进行比较。

USE [dbtest]
GO
/****** Object:  StoredProcedure [dbo].[sptest]
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
ALTER PROCEDURE [dbo].[sptest]
    
AS
BEGIN
    
    Select VTB.RFC as 'RFC',
           LRC.QTY as 'QTY',  
           PL.PRICE as 'Price',
           (PL.PRICE *LRC.QTY) * 1.16 as 'Total'
    from PACKING RC  
        inner join VTABLE VTB on VTB.COLUMN = RC.INVOICE
        inner join TRANS LRC on (LRC.PACKING = RC.RECID)
        inner join PURCH PL on (PL.NUMBER =LRC.NUM and PL.PURCH =RC.PURCH)      
END

我希望有人可以指导我使用我正在创建的这个功能。

更新:

为了提供更快的验证以使数据与 XML 的内容相同,现在我决定在前面获取它们,也就是说,在我加载的同一视图中显示它们XML如下:

<div class="form-group row">
                        <label for="lblNombreVisita" class="col-sm-3 col-form-label">RFC:</label>
                        <div class="col-sm-8">
                          <input type="text" class="form-control" id="txtRfc" disabled>
                        </div>
                     </div>
           <div class="form-group row">
                        <label for="lblNombreVisita" class="col-sm-3 col-form-label">Total:</label>
                        <div class="col-sm-8">
                          <input type="text" class="form-control" id="txtTotal" disabled>
                        </div>
                     </div>

<form action="" enctype="multipart/form-data">
              <div class="form-group row">
                            <label for="fileToUpload" class="col-sm-3 col-form-label"> Archivo XML:</label>
                  <div class="col-sm-8">
                    <input type="file" name="fileToUpload" id="XmlToUpload" class="btn" accept=".xml" required>
                  </div>
                        </div>
                        

              <div class="modal-footer">
                <button type="button" class="btn btn-secondary" data-dismiss="modal">Cancelar</button>
                <button type="button" id="upload" class="btn btn-success">Subir Documentos</button>
              </div>
          </form>

同样,如果有人发现如何验证它们与数据库中找到的值相等,那么它也是有效的,或者如果它们可以直接使用我在文本字段中获得的值进行验证我刚刚加了,也是有效的。

【问题讨论】:

  • XML 示例无效。它缺少命名空间定义。

标签: javascript php xml


【解决方案1】:

假设您共享的 XML 是传递给 SQL Server 的实际 XML,您将遇到命名空间方面的挑战。它们需要在 XML 中声明,然后在查询数据时再次声明。

这是一个示例,它返回您共享的 TotalRfc 的值。

-- String holding the supplied XML.

DECLARE @my_xml varchar(MAX) = 
'<?xml version="1.0" encoding="UTF-8"?>
<cfdi:Voucher Version="3.3" Total="45264.13">
  <cfdi:Issuer Rfc="ABC123456AB1" Name="JOHN DOE"/>
  <cfdi:Concepts>
    <cfdi:Concept UnitValue="93.80"></cfdi:Concept>
  </cfdi:Concepts>
  <cfdi:Plugin>
    <tfd:Digital Version="1.1" />
  </cfdi:Plugin>
</cfdi:Voucher>';

-- Remove the XML declaration and add the required namespaces via a "root" wrapper.
-- Note: You could use STUFF to place root after the declaration and keep it, but this was faster.

SELECT 
    @my_xml = REPLACE ( @my_xml, '<?xml version="1.0" encoding="UTF-8"?>', '' ),
    @my_xml = CONCAT ( '<root xmlns:cfdi="uri" xmlns:tfd="uri">', @my_xml, '</root>' );

-- Return the values for Total and Rfc.

SELECT
    t.c.value('declare namespace cfdi="uri"; (@Total)[1]', 'decimal(18,2)') AS Total,
    t.c.value('declare namespace cfdi="uri"; (cfdi:Issuer/@Rfc)[1]', 'varchar(50)') AS Rfc
FROM ( SELECT CAST ( @my_xml AS xml ) AS xml_data ) AS x
CROSS APPLY x.xml_data.nodes( 'declare namespace cfdi="uri"; //root/cfdi:Voucher' ) t(c);

退货

+----------+--------------+
|  Total   |     Rfc      |
+----------+--------------+
| 45264.13 | ABC123456AB1 |
+----------+--------------+

假设我了解您的验证和要求,您可以调整您的存储过程以接受您的 XML 作为参数,然后进行相应的验证/处理。

下面是一个例子供参考:

CREATE OR ALTER PROCEDURE [dbo].[sptest] (
    @my_xml varchar(MAX)   
)
AS
BEGIN
    
    DECLARE
        @rfc varchar(50), @qty int, @price decimal(18,2), @total decimal(18,2);

    /*****************************************/
    /*      Fetch values for validation      */
    /*****************************************/

    SELECT
        @rfc = VTB.RFC AS 'RFC',
        @qty = LRC.QTY AS 'QTY',  
        @price = PL.PRICE AS 'Price',
        @total = (PL.PRICE * LRC.QTY) * 1.16 AS 'Total'
    FROM PACKING AS RC  
    INNER JOIN VTABLE AS VTB 
        ON VTB.COLUMN = RC.INVOICE
    INNER JOIN TRANS AS LRC 
        ON (LRC.PACKING = RC.RECID)
    INNER JOIN PURCH AS PL 
        ON (PL.NUMBER =LRC.NUM and PL.PURCH =RC.PURCH)      

    /*****************************************/
    /*     Fetch XML values to validate      */
    /*****************************************/

    DECLARE
        @x_rfc varchar(50), @x_total decimal(18,2);

    -- Remove the XML declaration and add the required namespaces via a "root" wrapper.
    -- Note: You could use STUFF to place root after the declaration and keep it, but this was faster.

    SELECT 
        @my_xml = REPLACE ( @my_xml, '<?xml version="1.0" encoding="UTF-8"?>', '' ),
        @my_xml = CONCAT ( '<root xmlns:cfdi="uri" xmlns:tfd="uri">', @my_xml, '</root>' );

    -- Return the values for Total and Rfc.

    SELECT
        @x_total = t.c.value('declare namespace cfdi="uri"; (@Total)[1]', 'decimal(18,2)'),
        @x_rfc = t.c.value('declare namespace cfdi="uri"; (cfdi:Issuer/@Rfc)[1]', 'varchar(50)')
    FROM ( SELECT CAST ( @my_xml AS xml ) AS xml_data ) AS x
    CROSS APPLY x.xml_data.nodes( 'declare namespace cfdi="uri"; //root/cfdi:Voucher' ) t(c);

    /*****************************************/
    /*        Validate and handle XML        */
    /*****************************************/

    IF @rfc = @x_rfc AND @total = @x_total BEGIN

        -- Validation Success --
        -- Insert your XML to database...

    END ELSE BEGIN

        -- Validation Failure --
        -- Throw and error or whatever you need...

    END

END
GO

我在这里做了很多假设(比如你的 XML 和 SELECT 都只包含一条记录),但它应该能让你继续前进。

【讨论】:

  • 您的答案似乎有效,但是如何在我添加到问题中的代码中应用验证?我的意思是在执行此验证之前不允许加载XML文件
  • 您的存储过程可以负责验证和插入。我看到您在哪里选择数据,但没有看到您正在验证的内容。显示您的验证,我会看看我是否可以提供进一步的帮助。
  • 这是我需要的验证,我需要做一个验证,读取问题中指示的值并与数据库中表的数据进行比较,如果他们是正确的允许上传文件否则它会产生错误并且不允许它加载
  • 您只比较 RDC 和 Total 吗?它们是否需要等于从您的数据库中提取的值?
  • 没错,目前我有兴趣验证RFC和Total,它们必须等于从数据库中提取的值
【解决方案2】:

由于您想首先使用存储在数据库中的数据来验证 xml 数据,因此我建议您不要将 xml 文件移动到永久位置,而是阅读该文件并获取您想要验证的必填字段的值.要从 xml 文件中获取值,您必须使用任何 PHP 提供的 XML 库,例如 DOMDocumentXMLReaderSimpleXML,或者您可以使用此 XML2Array 将 xml 转换为关联数组以轻松获取价值

if (isset($_FILES["XmlToUpload"])) {
    // Create a unique filename
    $fileName = $_FILES["XmlToUpload"]["name"];
    $ext = pathinfo($fileName, PATHINFO_EXTENSION);
    $archivo = uniqid($fileName, true);
    $ruta ="XML/";
    
    $xmlData = file_get_contents($_FILES['XmlToUpload']['tmp_name']);
        
    $arr = XML2Array::createArray($xmlData);
    
    //Check whether this array contains the required keys or not i.e. Total & Rfc
    if (! isset($arr['cfdi:Voucher']['@attributes']['Total'])) {
        echo "Error";
        exit();
    }
    $totalAmount = $arr['cfdi:Voucher']['@attributes']['Total'];
    
    if (! isset($arr['cfdi:Voucher']['cfdi:Issuer']['@attributes']['Rfc'])) {
        echo "Error";
        exit();
    }
    $rfc = $arr['cfdi:Voucher']['cfdi:Issuer']['@attributes']['Rfc'];
    
    // Validating $totalAmount & $rfc with the data in database
    // Connect to database (Sql Server)
    $serverName = "serverName\\sqlexpress";
    $connectionInfo = array( "Database"=>"dbName", "UID"=>"userName", "PWD"=>"password");
    $conn = sqlsrv_connect( $serverName, $connectionInfo);
    
    if ($conn === false) {
        die( print_r( sqlsrv_errors(), true));
    }
    
    $sql = "Select VTB.RFC as 'RFC',
            LRC.QTY as 'QTY',  
            PL.PRICE as 'Price',
            (PL.PRICE *LRC.QTY) * 1.16 as 'Total'
        from PACKING RC  
            inner join VTABLE VTB on VTB.COLUMN = RC.INVOICE
            inner join TRANS LRC on (LRC.PACKING = RC.RECID)
            inner join PURCH PL on (PL.NUMBER =LRC.NUM and PL.PURCH =RC.PURCH)   
        where VTB.RFC = ? AND ((PL.PRICE * LRC.QTY) * 1.16) = ?";
    
    $params = array($rfc, $totalAmount);
    $options =  array( "Scrollable" => SQLSRV_CURSOR_KEYSET );
    $stmt = sqlsrv_query( $conn, $sql, $params, $options);
    $row_count = sqlsrv_num_rows( $stmt );
    
    if ($row_count === false) {
        echo "Error";
    } else {
        if(move_uploaded_file($_FILES["XmlToUpload"]["tmp_name"],$ruta.$archivo)){
            echo "Successfully uploaded";
        }else{
            echo "Error";
        }
    }  
} else {
    echo "A file was not selected";
}

【讨论】:

  • 当我尝试在我的开发中执行您的答案更改时,我发现此错误Fatal error: Class 'XML2Array' not found in C:\xampp\htdocs\GUI-PID\server.php on line 31 我还有什么需要添加到代码中的吗?
  • @user11804298 你需要从这里github.com/digitickets/lalit/blob/master/src/XML2Array.php 获取XML2Array 类才能使其正常工作
  • 除了我之前评论的错误之外,我看不到在哪里执行验证以等于数据库的值
  • @user11804298 我已经在代码中添加了注释,您必须在其中执行验证。
  • 验证是我所要求的,我通过与其他答案相同,我在 SQL Server 中执行所有代码但没有解决我的问题所需的初始验证,我的问题非常清楚地说明了如何读取 XML 文件并使用表中的数据进行验证,如果您能支持我进行所需的验证,我将不胜感激
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-12-02
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多