使用Golang和gorilla-xmlrpc进行增值税校验
使用Golang和gorilla-xmlrpc进行增值税校验 我正在尝试以以下格式向一个XML-RPC服务器发送POST请求:
https://evatr.bff-online.de/evatrRPC?UstId_1=DE123456789&UstId_2=AB1234567890 &Firmenname=Firmenname einschl. Rechtsform&Ort=Ort der Firma&PLZ=12345&Strasse=Strasse der Firma &Druck=ja
读取结果并在Go语言中通过名称访问它们:
一个示例结果XML如下所示:
<params>
<param>
<value><array><data>
<value><string>Datum</string></value>
<value><string>27.07.2006</string></value>
</data></array></value>
</param>
<param>
<value><array><data>
<value><string>Uhrzeit</string></value>
<value><string>13:35:53</string></value>
</data></array></value>
</param>
<param>
<value><array><data>
<value><string>ErrorCode</string></value>
<value><string>200</string></value>
</data></array></value>
</param>
<param>
<value><array><data>
<value><string>UstId_1</string></value>
<value><string>DE123456789</string></value>
</data></array></value>
</param>
<param>
<value><array><data>
<value><string>UstId_2</string></value>
<value><string>AB1234567890</string></value>
</data></array></value>
</param>
<param>
<value><array><data>
<value><string>Firmenname</string></value>
<value><string>Firma XY Rechtsform</string></value>
</data></array></value>
</param>
<param>
<value><array><data>
<value><string>Ort</string></value>
<value><string>Firmenort</string></value>
</data></array></value>
</param>
<param>
<value><array><data>
<value><string>PLZ</string></value>
<value><string>1234</string></value>
</data></array></value>
</param>
<param>
<value><array><data>
<value><string>Strasse</string></value>
<value><string>Firmenstrasse</string></value>
</data></array></value>
</param>
<param>
<value><array><data>
<value><string>Erg_Name</string></value>
<value><string>A</string></value>
</data></array></value>
</param>
<param>
<value><array><data>
<value><string>Erg_Ort</string></value>
<value><string>A</string></value>
</data></array></value>
</param>
<param>
<value><array><data>
<value><string>Erg_PLZ</string></value>
<value><string>A</string></value>
</data></array></value>
</param>
<param>
<value><array><data>
<value><string>Erg_Str</string></value>
<value><string>A</string></value>
</data></array></value>
</param>
<param>
<value><array><data>
<value><string>Gueltig_ab</string></value>
<value><string></string></value>
</data></array></value>
</param>
<param>
<value><array><data>
<value><string>Gueltig_bis</string></value>
<value><string></string></value>
</data></array></value>
</param>
<param>
<value><array><data>
<value><string>Druck</string></value>
<value><string>ja</string></value>
</data></array></value>
</param>
</params>
我目前的进展如下:
type Params struct {
XMLName xml2.Name `xml:"params"`
Text string `xml:",chardata"`
Param []struct {
Text string `xml:",chardata"`
Value struct {
Text string `xml:",chardata"`
Array struct {
Text string `xml:",chardata"`
Data struct {
Text string `xml:",chardata"`
Value []struct {
Text string `xml:",chardata"`
String string `xml:"string"`
} `xml:"value"`
} `xml:"data"`
} `xml:"array"`
} `xml:"value"`
} `xml:"param"`
}
func XmlRpcCall(method string, args struct{UstId_1,UstId_2,Firmenname,Ort,PLZ,Strasse,Druck string}) (reply struct{Message string}, err error) {
buf, _ := xml.EncodeClientRequest(method, &args)
resp, err := http.Post("https://evatr.bff-online.de/", "text/xml", bytes.NewBuffer(buf))
if err != nil {
return
}
defer resp.Body.Close()
err = xml.DecodeClientResponse(resp.Body, &reply)
return
}
func main() {
reply, err := XmlRpcCall("evatrRPC", struct{UstId_1,UstId_2,Firmenname,Ort,PLZ,Strasse,Druck string}{"VAT1","VAT2","Name","Town","ZIP","Street","No"})
if err != nil {
log.Fatal(err)
}
fmt.Println(reply.Message)
}
我的代码可以打印出XML消息,但我该如何将其解析到结构体中并访问例如错误代码值200呢?
我对Go语言还比较陌生,所以任何建议都会有帮助。
更多关于使用Golang和gorilla-xmlrpc进行增值税校验的实战教程也可以访问 https://www.itying.com/category-94-b0.html
你好
你应该使用 encoding/xml 包中的 Unmarshal 函数 https://golang.org/pkg/encoding/xml/#Unmarshal
可以参考这个页面上的示例。你可以使用相同的技术,通过定义较小的部分来构建那个庞大的结构体,否则它会过于复杂且难以理解。
你好
对于这个庞大的结构体,干得漂亮! 下面的代码行将打印 ErrorCode 和 200。我通过使用点号表示法来获取结构体的成员,并逐层深入到嵌套结构中。
fmt.Println(container.Param[2].Value.Array.Data.Value[0].String)
fmt.Println(container.Param[2].Value.Array.Data.Value[1].String)
如果你想遍历所有值,可以这样做:
for _, p := range container.Param {
for _, v := range p.Value.Array.Data.Value {
fmt.Println(v.String)
}
}
使用 for range 遍历切片时,第一个返回值是索引,但我想要的是值,它通常是第二个返回值。这里我使用了占位符变量 _(下划线),因为我不关心索引,只关心值。
你甚至可以在XML标签中使用 > 指令来使结构体更加简洁
type Params struct {
Param []struct {
String []string `xml:"value>array>data>value>string"`
} `xml:"param"`
}
并像这样打印 ErrorCode 和 300
fmt.Println(container.Param[2].String[0])
fmt.Println(container.Param[2].String[1])
我是在阅读文档时发现这一点的。
文本字符串
xml:",chardata"
你需要这些吗?如果省略它们,结构体会小很多吗?
type Params struct {
Param []struct {
Value struct {
Array struct {
Data struct {
Value []struct {
String string `xml:"string"`
} `xml:"value"`
} `xml:"data"`
} `xml:"array"`
} `xml:"value"`
} `xml:"param"`
}
就我所见,它运行得很好。
以下是用于检查欧洲增值税ID号的最终函数:
package main
import (
"bytes"
xml2 "encoding/xml"
"fmt"
"github.com/dannyvankooten/vat"
"github.com/divan/gorilla-xmlrpc/xml"
)
func checkVat(UstId_1, UstId_2, Firmenname, Ort, PLZ, Strasse, Druck string) {
validity, err := vat.ValidateNumberFormat(UstId_2)
if err != nil {
fmt.Println("Format der Umsatzsteuer ID unzulässig")
}
if validity == true {
fmt.Println("Format der Umsatzsteuer ID zulässig")
}
validity2, err2 := vat.ValidateNumberExistence(UstId_2)
if err2 != nil {
fmt.Println("Umsatzsteuer ID ist unzulässig")
}
if validity2 == true {
fmt.Println("Umsatzsteuer ID zulässig")
}
if validity && validity2 == true {
XmlRpcCall("evatrRPC", struct{ UstId_1, UstId_2, Firmenname, Ort, PLZ, Strasse, Druck string }{UstId_1, UstId_2, Firmenname, Ort, PLZ, Strasse, Druck})
}
}
func XmlRpcCall(method string, args struct{ UstId_1, UstId_2, Firmenname, Ort, PLZ, Strasse, Druck string }) {
type Params struct {
Param []struct {
Value struct {
Array struct {
Data struct {
Value []struct {
String string `xml:"string"`
} `xml:"value"`
} `xml:"data"`
} `xml:"array"`
} `xml:"value"`
} `xml:"param"`
}
type rep struct {
Message string
}
var reply rep
result := make(map[string]string)
var Key string
buf, _ := xml.EncodeClientRequest(method, &args)
resp, err := http.Post("https://evatr.bff-online.de/", "text/xml", bytes.NewBuffer(buf))
defer resp.Body.Close()
err = xml.DecodeClientResponse(resp.Body, &reply)
container := Params{}
err = xml2.Unmarshal([]byte(reply.Message), &container)
if err != nil {
fmt.Println("Error:", err)
}
for _, p := range container.Param {
for i, v := range p.Value.Array.Data.Value {
if i == 0 {
Key = v.String
}
if i == 1 {
result[Key] = v.String
}
}
}
if result["ErrorCode"] == "200" {
fmt.Println("Die angefragte USt-IdNr. ist gültig.")
} else {
fmt.Println("Die angefragte USt-IdNr. ist ungültig!")
}
fmt.Println(result)
return
}
func main() {
checkVat("YourVATId", "CustomerVATId", "Customer Company Name", "Customer City", "Customer Postcode", "Customer Street", "Print Yes/No")
}
checkVat 函数首先检查增值税ID号的格式是否正确,然后检查该号码是否已签发。如果两项检查都通过,它将根据客户地址请求验证该号码。"Print Yes/No"参数会触发德国税务部门发送一封包含您查询官方结果的信件。查询结果保存在一个名为result的映射中,其键对应于字段(UstId_1, UstId_2, Firmenname, Ort, PLZ, Strasse, Druck)。
希望其他人也能从中找到一些用处。如果对如何优化代码有任何建议,请告诉我。
我尝试按照那个例子,使用了这段代码,但我仍然无法单独访问这些值。例如,CodeError = 200 …
package main
import (
"encoding/xml"
"fmt"
)
var reply string
type Params struct {
XMLName xml.Name `xml:"params"`
Text string `xml:",chardata"`
Param []struct {
Text string `xml:",chardata"`
Value struct {
Text string `xml:",chardata"`
Array struct {
Text string `xml:",chardata"`
Data struct {
Text string `xml:",chardata"`
Value []struct {
Text string `xml:",chardata"`
String string `xml:"string"`
} `xml:"value"`
} `xml:"data"`
} `xml:"array"`
} `xml:"value"`
} `xml:"param"`
}
func main() {
reply = `
<params>
<param>
<value><array><data>
<value><string>Datum</string></value>
<value><string>27.07.2006</string></value>
</data></array></value>
</param>
<param>
<value><array><data>
<value><string>Uhrzeit</string></value>
<value><string>13:35:53</string></value>
</data></array></value>
</param>
<param>
<value><array><data>
<value><string>ErrorCode</string></value>
<value><string>200</string></value>
</data></array></value>
</param>
<param>
<value><array><data>
<value><string>UstId_1</string></value>
<value><string>DE123456789</string></value>
</data></array></value>
</param>
<param>
<value><array><data>
<value><string>UstId_2</string></value>
<value><string>AB1234567890</string></value>
</data></array></value>
</param>
<param>
<value><array><data>
<value><string>Firmenname</string></value>
<value><string>Firma XY Rechtsform</string></value>
</data></array></value>
</param>
<param>
<value><array><data>
<value><string>Ort</string></value>
<value><string>Firmenort</string></value>
</data></array></value>
</param>
<param>
<value><array><data>
<value><string>PLZ</string></value>
<value><string>1234</string></value>
</data></array></value>
</param>
<param>
<value><array><data>
<value><string>Strasse</string></value>
<value><string>Firmenstrasse</string></value>
</data></array></value>
</param>
<param>
<value><array><data>
<value><string>Erg_Name</string></value>
<value><string>A</string></value>
</data></array></value>
</param>
<param>
<value><array><data>
<value><string>Erg_Ort</string></value>
<value><string>A</string></value>
</data></array></value>
</param>
<param>
<value><array><data>
<value><string>Erg_PLZ</string></value>
<value><string>A</string></value>
</data></array></value>
</param>
<param>
<value><array><data>
<value><string>Erg_Str</string></value>
<value><string>A</string></value>
</data></array></value>
</param>
<param>
<value><array><data>
<value><string>Gueltig_ab</string></value>
<value><string></string></value>
</data></array></value>
</param>
<param>
<value><array><data>
<value><string>Gueltig_bis</string></value>
<value><string></string></value>
</data></array></value>
</param>
<param>
<value><array><data>
<value><string>Druck</string></value>
<value><string>ja</string></value>
</data></array></value>
</param>
</params>
`
container := Params{}
err := xml.Unmarshal([]byte(reply), &container)
if err != nil {
fmt.Println(err)
} else {
fmt.Println(container)
}
}
结果:
{{ params}
[{
{{{
[{ Datum} { 27.07.2006}]}}}} {{
{{{
[{ Uhrzeit} { 13:35:53}]}}}} {{
{{{
[{ ErrorCode} { 200}]}}}} {{
{{{
[{ UstId_1} { DE123456789}]}}}} {{
{{{
[{ UstId_2} { AB1234567890}]}}}} {{
{{{
[{ Firmenname} { Firma XY Rechtsform}]}}}} {{
{{{
[{ Ort} { Firmenort}]}}}} {{
{{{
[{ PLZ} { 1234}]}}}} {{
{{{
[{ Strasse} { Firmenstrasse}]}}}} {{
{{{
[{ Erg_Name} { A}]}}}} {{
{{{
[{ Erg_Ort} { A}]}}}} {{
{{{
[{ Erg_PLZ} { A}]}}}} {{
{{{
[{ Erg_Str} { A}]}}}} {{
{{{
[{ Gueltig_ab} { }]}}}} {{
{{{
[{ Gueltig_bis} { }]}}}} {{
{{{
[{ Druck} { ja}]}}}}]}
使用gorilla-xmlrpc处理增值税校验的响应,需要正确解析XML-RPC的嵌套数据结构。以下是完整的解决方案:
package main
import (
"bytes"
"encoding/xml"
"fmt"
"io"
"net/http"
"github.com/gorilla/xmlrpc"
)
// 定义响应数据结构
type VATResponse struct {
Datum string `xmlrpc:"Datum"`
Uhrzeit string `xmlrpc:"Uhrzeit"`
ErrorCode string `xmlrpc:"ErrorCode"`
UstId_1 string `xmlrpc:"UstId_1"`
UstId_2 string `xmlrpc:"UstId_2"`
Firmenname string `xmlrpc:"Firmenname"`
Ort string `xmlrpc:"Ort"`
PLZ string `xmlrpc:"PLZ"`
Strasse string `xmlrpc:"Strasse"`
Erg_Name string `xmlrpc:"Erg_Name"`
Erg_Ort string `xmlrpc:"Erg_Ort"`
Erg_PLZ string `xmlrpc:"Erg_PLZ"`
Erg_Str string `xmlrpc:"Erg_Str"`
Gueltig_ab string `xmlrpc:"Gueltig_ab"`
Gueltig_bis string `xmlrpc:"Gueltig_bis"`
Druck string `xmlrpc:"Druck"`
}
// 自定义XML-RPC解码器处理嵌套数组结构
func decodeVATResponse(data []byte) (*VATResponse, error) {
type param struct {
Value []string `xml:"array>data>value>string"`
}
type response struct {
XMLName xml.Name `xml:"params"`
Params []param `xml:"param"`
}
var resp response
if err := xml.Unmarshal(data, &resp); err != nil {
return nil, err
}
result := &VATResponse{}
for _, p := range resp.Params {
if len(p.Value) == 2 {
key := p.Value[0]
value := p.Value[1]
switch key {
case "Datum":
result.Datum = value
case "Uhrzeit":
result.Uhrzeit = value
case "ErrorCode":
result.ErrorCode = value
case "UstId_1":
result.UstId_1 = value
case "UstId_2":
result.UstId_2 = value
case "Firmenname":
result.Firmenname = value
case "Ort":
result.Ort = value
case "PLZ":
result.PLZ = value
case "Strasse":
result.Strasse = value
case "Erg_Name":
result.Erg_Name = value
case "Erg_Ort":
result.Erg_Ort = value
case "Erg_PLZ":
result.Erg_PLZ = value
case "Erg_Str":
result.Erg_Str = value
case "Gueltig_ab":
result.Gueltig_ab = value
case "Gueltig_bis":
result.Gueltig_bis = value
case "Druck":
result.Druck = value
}
}
}
return result, nil
}
func XmlRpcCall(method string, args map[string]string) (*VATResponse, error) {
// 使用gorilla-xmlrpc编码请求
buf, err := xmlrpc.EncodeClientRequest(method, args)
if err != nil {
return nil, err
}
// 发送请求
resp, err := http.Post("https://evatr.bff-online.de/evatrRPC", "text/xml", bytes.NewBuffer(buf))
if err != nil {
return nil, err
}
defer resp.Body.Close()
// 读取响应
body, err := io.ReadAll(resp.Body)
if err != nil {
return nil, err
}
// 解码响应
return decodeVATResponse(body)
}
func main() {
args := map[string]string{
"UstId_1": "DE123456789",
"UstId_2": "AB1234567890",
"Firmenname": "Firmenname einschl. Rechtsform",
"Ort": "Ort der Firma",
"PLZ": "12345",
"Strasse": "Strasse der Firma",
"Druck": "ja",
}
response, err := XmlRpcCall("evatrRPC", args)
if err != nil {
fmt.Printf("Error: %v\n", err)
return
}
// 访问具体字段
fmt.Printf("ErrorCode: %s\n", response.ErrorCode)
fmt.Printf("Datum: %s\n", response.Datum)
fmt.Printf("Uhrzeit: %s\n", response.Uhrzeit)
fmt.Printf("Firmenname: %s\n", response.Firmenname)
fmt.Printf("Erg_Name: %s\n", response.Erg_Name)
}
如果需要使用标准库的xml包而不依赖gorilla-xmlrpc,可以使用以下替代方案:
package main
import (
"bytes"
"encoding/xml"
"fmt"
"io"
"net/http"
"strings"
)
// 使用xml包直接解析
func parseVATResponseXML(xmlData string) (map[string]string, error) {
type StringValue struct {
String string `xml:"string"`
}
type Data struct {
Values []StringValue `xml:"value"`
}
type Array struct {
Data Data `xml:"data"`
}
type Value struct {
Array Array `xml:"array"`
}
type Param struct {
Value Value `xml:"value"`
}
type Params struct {
Params []Param `xml:"param"`
}
var result Params
err := xml.Unmarshal([]byte(xmlData), &result)
if err != nil {
return nil, err
}
dataMap := make(map[string]string)
for _, param := range result.Params {
if len(param.Value.Array.Data.Values) == 2 {
key := param.Value.Array.Data.Values[0].String
value := param.Value.Array.Data.Values[1].String
dataMap[key] = value
}
}
return dataMap, nil
}
func makeXMLRPCRequest(method string, params map[string]string) (string, error) {
var builder strings.Builder
builder.WriteString(`<?xml version="1.0"?>`)
builder.WriteString(`<methodCall>`)
builder.WriteString(`<methodName>` + method + `</methodName>`)
builder.WriteString(`<params>`)
for key, value := range params {
builder.WriteString(`<param><value><struct>`)
builder.WriteString(`<member>`)
builder.WriteString(`<name>` + key + `</name>`)
builder.WriteString(`<value><string>` + value + `</string></value>`)
builder.WriteString(`</member>`)
builder.WriteString(`</struct></value></param>`)
}
builder.WriteString(`</params>`)
builder.WriteString(`</methodCall>`)
return builder.String(), nil
}
func main() {
params := map[string]string{
"UstId_1": "DE123456789",
"UstId_2": "AB1234567890",
"Firmenname": "Firmenname einschl. Rechtsform",
"Ort": "Ort der Firma",
"PLZ": "12345",
"Strasse": "Strasse der Firma",
"Druck": "ja",
}
xmlRequest, err := makeXMLRPCRequest("evatrRPC", params)
if err != nil {
fmt.Printf("Error creating request: %v\n", err)
return
}
resp, err := http.Post("https://evatr.bff-online.de/evatrRPC", "text/xml",
bytes.NewBufferString(xmlRequest))
if err != nil {
fmt.Printf("Error making request: %v\n", err)
return
}
defer resp.Body.Close()
body, err := io.ReadAll(resp.Body)
if err != nil {
fmt.Printf("Error reading response: %v\n", err)
return
}
result, err := parseVATResponseXML(string(body))
if err != nil {
fmt.Printf("Error parsing response: %v\n", err)
return
}
// 访问具体字段
fmt.Printf("ErrorCode: %s\n", result["ErrorCode"])
fmt.Printf("Datum: %s\n", result["Datum"])
fmt.Printf("Firmenname: %s\n", result["Firmenname"])
fmt.Printf("Erg_Name: %s\n", result["Erg_Name"])
}
这两种方法都能正确解析XML-RPC响应并让你通过字段名访问数据,特别是ErrorCode字段。第一种方法使用gorilla-xmlrpc处理编码,第二种方法使用标准库手动构建请求。


