8

私はこのようにXMLを何年も解析してきましたが、さまざまな要素の数が増えると、少し退屈で疲れ果ててしまうことを認めなければなりません。つまり、ダミーXMLのサンプルを次に示します。

<?xml version="1.0"?>
<Order>
    <Date>2003/07/04</Date>
    <CustomerId>123</CustomerId>
    <CustomerName>Acme Alpha</CustomerName>
    <Item>
        <ItemId> 987</ItemId>
        <ItemName>Coupler</ItemName>
        <Quantity>5</Quantity>
    </Item>
    <Item>
        <ItemId>654</ItemId>
        <ItemName>Connector</ItemName>
        <Quantity unit="12">3</Quantity>
    </Item>
    <Item>
        <ItemId>579</ItemId>
        <ItemName>Clasp</ItemName>
        <Quantity>1</Quantity>
    </Item>
</Order>

これは関連する部分です(サックスを使用):

public class SaxParser extends DefaultHandler {

    boolean isItem = false;
    boolean isOrder = false;
    boolean isDate = false;
    boolean isCustomerId = false;
    private Order order;
    private Item item;

        @Override
    public void startElement(String namespaceURI, String localName, String qName, Attributes atts) {
        if (localName.equalsIgnoreCase("ORDER")) {
            order = new Order();
        }

        if (localName.equalsIgnoreCase("DATE")) {
            isDate = true;
        }

        if (localName.equalsIgnoreCase("CUSTOMERID")) {
            isCustomerId = true;
        }

        if (localName.equalsIgnoreCase("ITEM")) {
            isItem = true;
        }
    }

    public void characters(char ch[], int start, int length) throws SAXException {

        if (isDate){
            SimpleDateFormat formatter = new SimpleDateFormat("yyyy/MM/dd");
            String value = new String(ch, start, length);
            try {
                order.setDate(formatter.parse(value));
            } catch (ParseException e) {
                e.printStackTrace();
            }
        }

        if(isCustomerId){
            order.setCustomerId(Integer.valueOf(new String(ch, start, length)));
        }

        if (isItem) {
            item = new Item();
            isItem = false;
        }



    }

}

要素の数とともに成長し続けるこれらの恐ろしいブール値を取り除く方法があるのだろうかと思います。この比較的単純なxmlを解析するためのより良い方法があるはずです。このタスクを実行するために必要なコード行を見るだけでは、見苦しく見えます。

現在、SAXパーサーを使用していますが、他の提案も受け付けています(DOM以外では、メモリパーサーに巨大なXMLファイルを用意する余裕はありません)。

4

9 に答える 9

6

XMLの定義を制御する場合は、たとえばJAXB(XMLバインディング用のJavaアーキテクチャ)などのXMLバインディングツールを使用できます。JAXBでは、XML構造のスキーマを定義するか(XSDなどがサポートされます)、Javaに注釈を付けることができます。シリアル化ルールを定義するためのクラス。XMLとJavaの間の明確な宣言型マッピングができたら、XMLとの間のマーシャリングとアンマーシャリングは簡単になります。

JAXBを使用すると、SAXハンドラーよりも多くのメモリが必要になりますが、XMLドキュメントをパーツごとに処理する方法があります。大きなドキュメントの処理。

OracleのJAXBページ

于 2013-03-25T23:59:20.807 に答える
5

StAXでJAXBを使用する例を次に示します。

入力ドキュメント:

<?xml version="1.0" encoding="UTF-8"?>
<Personlist xmlns="http://example.org">
    <Person>
        <Name>Name 1</Name>
        <Address>
            <StreetAddress>Somestreet</StreetAddress>
            <PostalCode>00001</PostalCode>
            <CountryName>Finland</CountryName>
        </Address>
    </Person>
    <Person>
        <Name>Name 2</Name>
        <Address>
            <StreetAddress>Someotherstreet</StreetAddress>
            <PostalCode>43400</PostalCode>
            <CountryName>Sweden</CountryName>
        </Address>
    </Person>
</Personlist>

Person.java:

@XmlRootElement(name = "Person", namespace = "http://example.org")
public class Person {
    @XmlElement(name = "Name", namespace = "http://example.org")
    private String name;
    @XmlElement(name = "Address", namespace = "http://example.org")
    private Address address;

    public String getName() {
        return name;
    }

    public Address getAddress() {
        return address;
    }
}

Address.java:

public class Address {
    @XmlElement(name = "StreetAddress", namespace = "http://example.org")
    private String streetAddress;
    @XmlElement(name = "PostalCode", namespace = "http://example.org")
    private String postalCode;
    @XmlElement(name = "CountryName", namespace = "http://example.org")
    private String countryName;

    public String getStreetAddress() {
        return streetAddress;
    }

    public String getPostalCode() {
        return postalCode;
    }

    public String getCountryName() {
        return countryName;
    }
}

PersonlistProcessor.java:

public class PersonlistProcessor {
    public static void main(String[] args) throws Exception {
        new PersonlistProcessor().processPersonlist(PersonlistProcessor.class
                .getResourceAsStream("personlist.xml"));
    }

    // TODO: Instead of throws Exception, all exceptions should be wrapped
    // inside runtime exception
    public void processPersonlist(InputStream inputStream) throws Exception {
        JAXBContext jaxbContext = JAXBContext.newInstance(Person.class);
        XMLStreamReader xss = XMLInputFactory.newFactory().createXMLStreamReader(inputStream);
        // Create unmarshaller
        Unmarshaller unmarshaller = jaxbContext.createUnmarshaller();
        // Go to next tag
        xss.nextTag();
        // Require Personlist
        xss.require(XMLStreamReader.START_ELEMENT, "http://example.org", "Personlist");
        // Go to next tag
        while (xss.nextTag() == XMLStreamReader.START_ELEMENT) {
            // Require Person
            xss.require(XMLStreamReader.START_ELEMENT, "http://example.org", "Person");
            // Unmarshall person
            Person person = (Person)unmarshaller.unmarshal(xss);
            // Process person
            processPerson(person);
        }
        // Require Personlist
        xss.require(XMLStreamReader.END_ELEMENT, "http://example.org", "Personlist");
    }

    private void processPerson(Person person) {
        System.out.println(person.getName());
        System.out.println(person.getAddress().getCountryName());
    }
}
于 2013-03-26T11:26:42.290 に答える
0

SAXでは、パーサーがハンドラーでイベントを「プッシュ」するため、ここで慣れているように、すべてのハウスキーピングを実行する必要があります。別の方法はStAX(javax.xml.streamパッケージ)です。これはまだストリーミング中ですが、コードがパーサーからのイベントの「プル」を担当します。このように、どの要素がどの順序で期待されるかのロジックは、ブール値で明示的に表現される必要はなく、プログラムの制御フローでエンコードされます。

XMLの正確な構造によっては、 XOMなどのツールキットを使用する「中道」が存在する場合があります。このツールキットには、ドキュメントのサブツリーをDOMのようなオブジェクトモデルに解析し、その小枝を処理してからスローする操作モードがあります。それを離れて、次のものを解析します。これは、それぞれを個別に処理できる多くの類似した要素を持つ反復ドキュメントに適しています。各小枝内のツリーベースのAPIへのプログラミングが容易でありながら、巨大なドキュメントを効率的に解析できるストリーミング動作があります。

public class ItemProcessor extends NodeFactory {
  private Nodes emptyNodes = new Nodes();

  public Nodes finishMakingElement(Element elt) {
    if("Item".equals(elt.getLocalName())) {
      // process the Item element here
      System.out.println(elt.getFirstChildElement("ItemId").getValue()
         + ": " + elt.getFirstChildElement("ItemName").getValue());

      // then throw it away
      return emptyNodes;
    } else {
      return super.finishMakingElement(elt);
    }
  }
}

StAXとJAXBの組み合わせで同様のことを実現できます-繰り返し要素(この例ではItem)を表すJAXB注釈付きクラスを定義してから、StAXパーサーを作成し、最初のItem開始タグに移動して、1つを完全にアンマーシャリングできますItemから一度にXMLStreamReader

于 2013-03-25T23:48:02.343 に答える
0

私はxsteamを使用して自分のオブジェクトをxmlにシリアル化し、Javaオブジェクトとしてロードし直しています。EverythignをPOJOとして表すことができ、xmlファイルのタイプと一致するようにPOJOに適切に注釈を付けると、はるかに使いやすくなる可能性があります。

文字列がXMLでオブジェクトを表す場合、次のように記述できます。

Order theOrder = (Order)xstream.fromXML(xmlString);

私は常にオブジェクトを1行でメモリにロードするために使用していましたが、オブジェクトをストリーミングして処理する必要がある場合は、 HierarchicalStreamReaderを使用してドキュメントを反復処理できるはずです。これは、@Daveによって提案されたSimpleと非常に似ている可能性があります。

于 2013-03-26T00:06:19.353 に答える
0

他の人が示唆しているように、Staxモデルはプッシュベースのモデルであるため、メモリーフットプリントを最小限に抑えるためのより良いアプローチです。私は個人的にAxio(Apache Axisで使用されています)を使用し、提供されたコードスニペットで行ったようにノード要素を通過するよりも冗長ではないXPath式を使用して要素を解析しました。

于 2013-03-26T00:21:02.647 に答える
0

私はこのライブラリを使用しています。これは、標準のJavaライブラリの上にあり、作業を簡単にします。特に、説明した大きな「if」ステートメントを使用するのではなく、名前で特定の要素または属性を要求できます。

http://marketmovers.blogspot.com/2014/02/the-easy-way-to-read-xml-in-java.html

于 2014-02-18T21:27:06.033 に答える
0

よりコンパクトなXML解析をサポートする別のライブラリであるRTXMLがあります。ライブラリとそのドキュメントはrasmustorkel.comにあります。元の質問でファイルの解析を実装し、ここに完全なプログラムを含めています。

package for_so;

import java.io.File;
import java.util.ArrayList;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import rasmus_torkel.xml_basic.read.TagNode;
import rasmus_torkel.xml_basic.read.XmlReadOptions;
import rasmus_torkel.xml_basic.read.impl.XmlReader;

public class Q15626686_ReadOrder
{
    public static class Order
    {
        public final Date            _date;
        public final int             _customerId;
        public final String          _customerName;
        public final ArrayList<Item> _itemAl;

        public
        Order(TagNode node)
        {
            _date = (Date)node.nextStringMappedFieldE("Date", Date.class);
            _customerId = (int)node.nextIntFieldE("CustomerId");
            _customerName = node.nextTextFieldE("CustomerName");
            _itemAl = new ArrayList<Item>();
            boolean finished = false;
            while (!finished)
            {
                TagNode itemNode = node.nextChildN("Item");
                if (itemNode != null)
                {
                    Item item = new Item(itemNode);
                    _itemAl.add(item);
                }
                else
                {
                    finished = true;
                }
            }
            node.verifyNoMoreChildren();
        }
    }

    public static final Pattern DATE_PATTERN = Pattern.compile("^(\\d\\d\\d\\d)\\/(\\d\\d)\\/(\\d\\d)$");

    public static class Date
    {
        public final String _dateString;
        public final int    _year;
        public final int    _month;
        public final int    _day;

        public
        Date(String dateString)
        {
            _dateString = dateString;
            Matcher matcher = DATE_PATTERN.matcher(dateString);
            if (!matcher.matches())
            {
                throw new RuntimeException(dateString + " does not match pattern " + DATE_PATTERN.pattern());
            }
            _year = Integer.parseInt(matcher.group(1));
            _month = Integer.parseInt(matcher.group(2));
            _day = Integer.parseInt(matcher.group(3));
        }
    }

    public static class Item
    {
        public final int      _itemId;
        public final String   _itemName;
        public final Quantity _quantity;

        public
        Item(TagNode node)
        {
            _itemId = node.nextIntFieldE("ItemId");
            _itemName = node.nextTextFieldE("ItemName");
            _quantity = new Quantity(node.nextChildE("Quantity"));
            node.verifyNoMoreChildren();
        }
    }

    public static class Quantity
    {
        public final int _unitSize;
        public final int _unitQuantity;

        public
        Quantity(TagNode node)
        {
            _unitSize = node.attributeIntD("unit", 1);
            _unitQuantity = node.onlyInt();
        }
    }

    public static void
    main(String[] args)
    {
        File xmlFile = new File(args[0]);
        TagNode orderNode = XmlReader.xmlFileToRoot(xmlFile, "Order", XmlReadOptions.DEFAULT);
        Order order = new Order(orderNode);
        System.out.println("Read order for " + order._customerName + " which has " + order._itemAl.size() + " items");
    }
}

検索関数がN、E、またはDで終わることに気付くでしょう。これらは、目的のデータ項目がそこにない場合の処理​​を参照します。NはreturnNullを表し、Eはthrow Exceptionを表し、DはuseDefaultを表します。

于 2016-04-08T12:44:20.933 に答える
0

外部パッケージ、またはXPathを使用しないソリューション:「 PARSE_MODE」を、おそらく:enumと組み合わせて使用​​します。Stack<PARSE_MODE>

1)基本的な解決策:

a)フィールド

private PARSE_MODE parseMode = PARSE_MODE.__UNDEFINED__;
// NB: essential that all these enum values are upper case, but this is the convention anyway
private enum PARSE_MODE {
    __UNDEFINED__, ORDER, DATE, CUSTOMERID, ITEM };
private List<String> parseModeStrings = new ArrayList<String>();
private Stack<PARSE_MODE> modeBreadcrumbs = new Stack<PARSE_MODE>();

b)List<String>おそらくコンストラクターで、を作成します。

    for( PARSE_MODE pm : PARSE_MODE.values() ){
        // might want to check here that these are indeed upper case
        parseModeStrings.add( pm.name() );
    }

c)startElementおよびendElement

@Override
public void startElement(String namespaceURI, String localName, String qName, Attributes atts) {
    String localNameUC = localName.toUpperCase();
    // pushing "__UNDEFINED__" would mess things up! But unlikely name for an XML element
    assert ! localNameUC.equals( "__UNDEFINED__" );

    if( parseModeStrings.contains( localNameUC )){
        parseMode = PARSE_MODE.valueOf( localNameUC );
        // any "policing" to do with which modes are allowed to switch into 
        // other modes could be put here... 
        // in your case, go `new Order()` here when parseMode == ORDER
        modeBreadcrumbs.push( parseMode );
    } 
    else {
       // typically ignore the start of this element...
    }
}   

@Override
private void endElement(String uri, String localName, String qName) throws Exception {
    String localNameUC = localName.toUpperCase();
    if( parseModeStrings.contains( localNameUC )){
        // will not fail unless XML structure which is malformed in some way
        // or coding error in use of the Stack, etc.:
        assert modeBreadcrumbs.pop() == parseMode;
        if( modeBreadcrumbs.empty() ){
            parseMode = PARSE_MODE.__UNDEFINED__;
        }
        else {
            parseMode = modeBreadcrumbs.peek();
        }
    } 
    else {
       // typically ignore the end of this element...
    }

}

...では、これはどういう意味ですか?いつでも、現在の「解析モード」についての知識があります...また、Stack<PARSE_MODE> modeBreadcrumbsここに到達するために通過した他の解析モードを確認する必要があるかどうかを確認することもできます...

その後characters、メソッドは大幅にクリーンになります。

public void characters(char[] ch, int start, int length) throws SAXException {
    switch( parseMode ){
    case DATE:
        // PS - this SimpleDateFormat object can be a field: it doesn't need to be created hundreds of times
        SimpleDateFormat formatter. ...
        String value = ...
        ...
        break;

    case CUSTOMERID:
        order.setCustomerId( ...
        break;

    case ITEM:
        item = new Item();
        // this next line probably won't be needed: when you get to endElement, if 
        // parseMode is ITEM, the previous mode will be restored automatically
        // isItem = false ;
    }

}

2)より「プロフェッショナルな」ソリューション:
abstract具体的なクラスを拡張する必要があり、その後、を変更する機能がないクラスStackなど。NBqNameではなくこれが調べますlocalName。したがって:

public abstract class AbstractSAXHandler extends DefaultHandler {
    protected enum PARSE_MODE implements SAXHandlerParseMode {
        __UNDEFINED__
    };
    // abstract: the concrete subclasses must populate...
    abstract protected Collection<Enum<?>> getPossibleModes();
    // 
    private Stack<SAXHandlerParseMode> modeBreadcrumbs = new Stack<SAXHandlerParseMode>();
    private Collection<Enum<?>> possibleModes;
    private Map<String, Enum<?>> nameToEnumMap;
    private Map<String, Enum<?>> getNameToEnumMap(){
        // lazy creation and population of map
        if( nameToEnumMap == null ){
            if( possibleModes == null ){
                possibleModes = getPossibleModes();
            }
            nameToEnumMap = new HashMap<String, Enum<?>>();
            for( Enum<?> possibleMode : possibleModes ){
                nameToEnumMap.put( possibleMode.name(), possibleMode ); 
            }
        }
        return nameToEnumMap;
    }

    protected boolean isLegitimateModeName( String name ){
        return getNameToEnumMap().containsKey( name );
    }

    protected SAXHandlerParseMode getParseMode() {
        return modeBreadcrumbs.isEmpty()? PARSE_MODE.__UNDEFINED__ : modeBreadcrumbs.peek();
    }

    @Override
    public void startElement(String uri, String localName, String qName, Attributes attributes)
            throws SAXException {
        try {
            _startElement(uri, localName, qName, attributes);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    // override in subclasses (NB I think caught Exceptions are not a brilliant design choice in Java)
    protected void _startElement(String uri, String localName, String qName, Attributes attributes)
            throws Exception {
        String qNameUC = qName.toUpperCase();
        // very undesirable ever to push "UNDEFINED"! But unlikely name for an XML element
        assert !qNameUC.equals("__UNDEFINED__") : "Encountered XML element with qName \"__UNDEFINED__\"!";
        if( getNameToEnumMap().containsKey( qNameUC )){
            Enum<?> newMode = getNameToEnumMap().get( qNameUC );
            modeBreadcrumbs.push( (SAXHandlerParseMode)newMode );
        }
    }

    @Override
    public void endElement(String uri, String localName, String qName) throws SAXException {
        try {
            _endElement(uri, localName, qName);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    // override in subclasses
    protected void _endElement(String uri, String localName, String qName) throws Exception {
        String qNameUC = qName.toUpperCase();
        if( getNameToEnumMap().containsKey( qNameUC )){
            modeBreadcrumbs.pop(); 
        }
    }

    public List<?> showModeBreadcrumbs(){
        return org.apache.commons.collections4.ListUtils.unmodifiableList( modeBreadcrumbs );
    }

}

interface SAXHandlerParseMode {

}

次に、具体的なサブクラスの顕著な部分:

private enum PARSE_MODE implements SAXHandlerParseMode {
    ORDER, DATE, CUSTOMERID, ITEM
};

private Collection<Enum<?>> possibleModes;

@Override
protected Collection<Enum<?>> getPossibleModes() {
    // lazy initiation
    if (possibleModes == null) {
        List<SAXHandlerParseMode> parseModes = new ArrayList<SAXHandlerParseMode>( Arrays.asList(PARSE_MODE.values()) );
        possibleModes = new ArrayList<Enum<?>>();
        for( SAXHandlerParseMode parseMode : parseModes ){
            possibleModes.add( PARSE_MODE.valueOf( parseMode.toString() ));
        }
        // __UNDEFINED__ mode (from abstract superclass) must be added afterwards
        possibleModes.add( AbstractSAXHandler.PARSE_MODE.__UNDEFINED__ );
    }
    return possibleModes;
}

PSこれは、より洗練されたものの開始点です。たとえば、:と同期を維持するを設定するとList<Object>、任意の値になり、上位の「XMLノード」に「到達」できるようになります。あなたが扱っているもの。ただし、を使用しないでください。は、同じオブジェクトを複数回含む可能性があります。これは実際、すべてのツリーのような構造の基本的な特性を示しています。個々のノード(ここでは解析モード)が分離して存在することはありません。そのIDは、常にそれにつながるパス全体によって定義されますStack<PARSE_MODE>ObjectsMapStackPARSE_MODE

于 2017-03-04T13:11:26.583 に答える
-1
    import java.io.File;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.ArrayList;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;
import javax.xml.xpath.XPath;
import javax.xml.xpath.XPathConstants;
import javax.xml.xpath.XPathExpression;
import javax.xml.xpath.XPathFactory;
import org.w3c.dom.Document;
import org.w3c.dom.NodeList;

public class JXML {
private DocumentBuilder builder;
private Document doc = null;
private DocumentBuilderFactory factory ;
private XPathExpression expr = null;
private XPathFactory xFactory;
private XPath xpath;
private String xmlFile;
public static ArrayList<String> XMLVALUE ;  


public JXML(String xmlFile){
    this.xmlFile = xmlFile;
}


private void xmlFileSettings(){     
    try {
        factory = DocumentBuilderFactory.newInstance();
        factory.setNamespaceAware(true);
        xFactory = XPathFactory.newInstance();
        xpath = xFactory.newXPath();
        builder = factory.newDocumentBuilder();
        doc = builder.parse(xmlFile);
    }
    catch (Exception e){
        System.out.println(e);
    }       
}



public String[] selectQuery(String query){
    xmlFileSettings();
    ArrayList<String> records = new ArrayList<String>();
    try {
        expr = xpath.compile(query);
        Object result = expr.evaluate(doc, XPathConstants.NODESET);
        NodeList nodes = (NodeList) result;
        for (int i=0; i<nodes.getLength();i++){             
            records.add(nodes.item(i).getNodeValue());
        }
        return records.toArray(new String[records.size()]);
    } 
    catch (Exception e) {
        System.out.println("There is error in query string");
        return records.toArray(new String[records.size()]);
    }       
}

public boolean updateQuery(String query,String value){
    xmlFileSettings();
    try{
        NodeList nodes = (NodeList) xpath.evaluate(query, doc, XPathConstants.NODESET);
        for (int idx = 0; idx < nodes.getLength(); idx++) {
          nodes.item(idx).setTextContent(value);
        }
        Transformer xformer = TransformerFactory.newInstance().newTransformer();
        xformer.transform(new DOMSource(doc), new StreamResult(new File(this.xmlFile)));
        return true;
    }catch(Exception e){
        System.out.println(e);
        return false;
    }
}




public static void main(String args[]){
    JXML jxml = new JXML("c://user.xml");
    jxml.updateQuery("//Order/CustomerId/text()","222");
    String result[]=jxml.selectQuery("//Order/Item/*/text()");
    for(int i=0;i<result.length;i++){
        System.out.println(result[i]);
    }
}

}

于 2013-03-25T23:57:27.747 に答える