Go中的常规XML解析器

时间:2016-01-18 16:01:40

标签: xml go xml-parsing

是否有一些在Go中读取XML文档的通用方法?类似于C#中的XmlDocument或XDocument?

我发现的所有示例都显示了如何使用解组功能读取我需要定义的对象,但是由于我需要定义很多我不会去的工作人员,所以它非常耗时。使用

xml.Unmarshal(...)

另一种方法是仅使用以下方式进行阅读:

xml.NewDecoder(xmlFile) 

此处描述:documentation for the math module

2 个答案:

答案 0 :(得分:3)

  

我发现的所有示例都显示了如何使用解组功能读取我需要定义的对象,但是由于我需要定义很多我不会去的工作人员,所以它非常耗时。使用

然后定义您将不会使用的内容,仅定义 您将要使用的内容。您不必创建完全涵盖XML结构的Go模型。

假设你有这样的XML:

import UIKit


class MealViewController: UIViewController, UITextFieldDelegate, 

UIImagePickerControllerDelegate, UINavigationControllerDelegate {

// MARK: Properties

@IBOutlet weak var nameTextField: UITextField!
@IBOutlet weak var photoImageView: UIImageView!
@IBOutlet weak var ratingControl: RatingControl!
@IBOutlet weak var usernameLabel: UILabel!

override func viewDidLoad() {
    super.viewDidLoad()


    // Handle the text field’s user input through delegate callbacks.
    nameTextField.delegate = self

    let attributes = [
        NSForegroundColorAttributeName: UIColor.blueColor(),
        NSFontAttributeName: UIFont(name: "Avenir", size: 2)!
    ]
    self.navigationController?.navigationBar.titleTextAttributes = attributes

}



// MARK: UITextFieldDelegate

func textFieldShouldReturn(textField: UITextField) -> Bool {
    // Hide the keyboard.
    textField.resignFirstResponder()
    return true
}

func textFieldDidEndEditing(textField: UITextField) {

}

// MARK: UIImagePickerControllerDelegate
func imagePickerControllerDidCancel(picker: UIImagePickerController) {
    // Dismiss the picker if the user canceled.
    dismissViewControllerAnimated(true, completion: nil)
}

func imagePickerController(picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [String : AnyObject]) {
    // The info dictionary contains multiple representations of the image, and this uses the original.
    let selectedImage = info[UIImagePickerControllerOriginalImage] as! UIImage

    // Set photoImageView to display the selected image.
    photoImageView.image = selectedImage

    print("my image ", photoImageView)

    // Dismiss the picker.
    dismissViewControllerAnimated(true, completion: nil)
}

// MARK: Actions
@IBAction func selectImageFromPhotoLibrary(sender: UITapGestureRecognizer) {
    // Hide the keyboard.
    //nameTextField.resignFirstResponder()

    // UIImagePickerController is a view controller that lets a user pick media from their photo library.
    let imagePickerController = UIImagePickerController()

    // Only allow photos to be picked, not taken.
    imagePickerController.sourceType = .PhotoLibrary

    // Make sure ViewController is notified when the user picks an image.
    imagePickerController.delegate = self

    presentViewController(imagePickerController, animated: true, completion: nil)
}

override func viewDidAppear(animated: Bool) {
    super.viewDidAppear(true)

    let prefs:NSUserDefaults = NSUserDefaults.standardUserDefaults()
    let isLoggedIn:Int = prefs.integerForKey("ISLOGGEDIN") as Int
    if (isLoggedIn != 1) {
        self.performSegueWithIdentifier("goto_login", sender: self)
    } else {
        self.usernameLabel.text = prefs.valueForKey("USERNAME") as? String
    }
}


@IBAction func ratingSubmitButton(sender: UIButton) {

    print(ratingControl.rating)
    print(self.usernameLabel.text!)


    let name:NSString = self.usernameLabel.text!
    let value = ratingControl.rating


    let imageData = UIImagePNGRepresentation(photoImageView.image!)



    do {
        let post:NSString = "name=\(name)&value=\(value)"

        NSLog("PostData: %@",post);

        let url:NSURL = NSURL(string:"http://kiran.com/insert.php")!

        let postData:NSData = post.dataUsingEncoding(NSASCIIStringEncoding)!

        let postLength:NSString = String( postData.length )




        let request:NSMutableURLRequest = NSMutableURLRequest(URL: url)
        request.HTTPMethod = "POST"
        request.HTTPBody = postData
        request.setValue(postLength as String, forHTTPHeaderField: "Content-Length")
        request.setValue("application/x-www-form-urlencoded", forHTTPHeaderField: "Content-Type")
        request.setValue("application/json", forHTTPHeaderField: "Accept")




        var reponseError: NSError?
        var response: NSURLResponse?

        var urlData: NSData?
        do {
            urlData = try NSURLConnection.sendSynchronousRequest(request, returningResponse:&response)
        } catch let error as NSError {
            reponseError = error
            urlData = nil
        }

        if ( urlData != nil ) {
            let res = response as! NSHTTPURLResponse!;

            NSLog("Response code: %ld", res.statusCode);

            if (res.statusCode >= 200 && res.statusCode < 300)
            {
                let responseData:NSString  = NSString(data:urlData!, encoding:NSUTF8StringEncoding)!

                NSLog("Response ==> %@", responseData);

                //var error: NSError?

                let jsonData:NSDictionary = try NSJSONSerialization.JSONObjectWithData(urlData!, options:NSJSONReadingOptions.MutableContainers ) as! NSDictionary


                let success:NSInteger = jsonData.valueForKey("success") as! NSInteger

                //[jsonData[@"success"] integerValue];

                NSLog("Success: %ld", success);

                if(success == 1)
                {
                    NSLog("Login SUCCESS");

                    let prefs:NSUserDefaults = NSUserDefaults.standardUserDefaults()
                    prefs.setObject(name, forKey: "USERNAME")
                    prefs.setInteger(1, forKey: "ISLOGGEDIN")
                    prefs.synchronize()

                    self.performSegueWithIdentifier("goto_rating", sender: self)
                } else {
                    var error_msg:NSString

                    if jsonData["error_message"] as? NSString != nil {
                        error_msg = jsonData["error_message"] as! NSString
                    } else {
                        error_msg = "Unknown Error"
                    }
                    let alertView:UIAlertView = UIAlertView()
                    alertView.title = "Rating Failed"
                    alertView.message = error_msg as String
                    alertView.delegate = self
                    alertView.addButtonWithTitle("OK")
                    alertView.show()

                }

            } else {
                }
        } else {
            }
    } catch {
        let alertView:UIAlertView = UIAlertView()
        alertView.title = "Rating Success!"
        alertView.message = "Thank You"
        alertView.delegate = self
        alertView.addButtonWithTitle("OK")
        alertView.show()
    }

}













@IBAction func logOutButton(sender: UIButton) {

    let appDomain = NSBundle.mainBundle().bundleIdentifier
    NSUserDefaults.standardUserDefaults().removePersistentDomainForName(appDomain!)

    self.performSegueWithIdentifier("goto_login", sender: self)
}

让我们假设您只需要以下XML信息:

  • ID
  • 关键字
  • 博客名称
  • 作者姓名

您可以使用以下结构对这些想要的信息进行建模:

<blog id="1234">
    <meta keywords="xml,parsing,partial" />
    <name>Partial XML parsing</name>
    <url>http://somehost.com/xml-blog</url>
    <entries count="2">
        <entry time="2016-01-19 08:40:00">
            <author>Bob</author>
            <content>First entry</content>
        </entry>
        <entry time="2016-01-19 08:30:00">
            <author>Alice</author>
            <content>Second entry</content>
        </entry>
    </entries>
</blog>

现在,您只能使用以下代码解析这些信息:

type Data struct {
    Id   string `xml:"id,attr"`
    Meta struct {
        Keywords string `xml:"keywords,attr"`
    } `xml:"meta"`
    Name    string   `xml:"name"`
    Authors []string `xml:"entries>entry>author"`
}

输出(在Apple docs上尝试):

d := Data{}
if err := xml.Unmarshal([]byte(s), &d); err != nil {
    panic(err)
}
fmt.Printf("%+v", d)

答案 1 :(得分:2)

嗯,有两件事。

首先,您没有义务定义映射到复杂元素的Go类型,以解析除encoding/xml之外的任何内容。 相反,您可以纯粹在程序上解析XML文档并仅在原始(非嵌套)元素上调用xml.Unmarshal() - 将它们解析为“原始”类型的值(例如string或{{1 }或int32等)。

这肯定会是很多代码,但这只是从更动态的角度来解决同样的问题。要理解我的意思,请以DOM对象的形式考虑完全解析的XML文档。要从中提取有用的数据,您必须以某种方式查询该对象或在树上进行迭代。使用您引用的博客文章的方法,您在解析它时遍历XML文档 - 基本上将解析与查询/遍历相结合。

这可能适用于您,也可能不适合您,因为解析XML格式数据的特定方法的适用性在很大程度上取决于其结构及其解析的预期结果。例如,如果您需要对文档执行多个查询,而后续查询取决于前者,则该博客文章的过程解码几乎不起作用。

第二,存在替代库。例如,请查看xmltreexmlpath。 虽然这两个是用纯Go编写的,但是存在一些包裹time.Time的包,例如goxml。有了它们,您可以根据需要进行面向DOM的解析。

另一种方法是使用mxj将XML解析为一组嵌套的键/值映射。