Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/java/372.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181

Warning: file_get_contents(/data/phpspider/zhask/data//catemap/0/xml/12.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Java XML节点值的快速替换_Java_Xml_Regex_Xslt - Fatal编程技术网

Java XML节点值的快速替换

Java XML节点值的快速替换,java,xml,regex,xslt,Java,Xml,Regex,Xslt,我有一堆XML文档,其中包含我需要用虚假数据替换的个人信息。Person节点包含以下元素: uuid-必需,不应触摸 名字-可选 lastName-可选 地址-可选 personID-必需 一个人可能出现多次,在这种情况下,应使用相同的假数据,即,如果两个人节点具有相同的personID,则他们都应接收相同的假ID 我已经实现了一些Java代码,这些代码从XML字符串构建DOM树,并在将其写回字符串之前替换节点。这很好,但由于我有这么多文档,我想知道是否有更快的方法。可能是通过正则表达式或X

我有一堆XML文档,其中包含我需要用虚假数据替换的个人信息。Person节点包含以下元素:

  • uuid-必需,不应触摸
  • 名字-可选
  • lastName-可选
  • 地址-可选
  • personID-必需
一个人可能出现多次,在这种情况下,应使用相同的假数据,即,如果两个人节点具有相同的personID,则他们都应接收相同的假ID

我已经实现了一些Java代码,这些代码从XML字符串构建DOM树,并在将其写回字符串之前替换节点。这很好,但由于我有这么多文档,我想知道是否有更快的方法。可能是通过正则表达式或XSLT之类的

以下是一个示例文档:

<ADocument>
  <Stuff>
    ...
  </Stuff>
  <OtherStuff>
    ...
  </OtherStuff>
  <Person>
    <uuid>11111111-1111-1111-1111-111111111111</uuid>
    <firstName>FAKEDATA0</firstName>
    <lastName>FAKEDATA0</lastName>
    <personID>000000000000</personID>
  </Person>
  <Person>
    <uuid>22222222-2222-2222-2222-222222222222</uuid>
    <firstName>FAKEDATA1</firstName>
    <address>FAKEDATA1</address>
    <personID>000000000001</personID>
  </Person>
  <Person>
    <uuid>33333333-3333-3333-3333-333333333333</uuid>
    <firstName>FAKEDATA0</firstName>
    <lastName>FAKEDATA0</lastName>
    <personID>000000000000</personID>
  </Person>
  <MoreStuff>
    ...
  </MoreStuff>
</ADocument>

...
...
11111111-1111-1111-1111-111111111111
一些
人
111111111111
22222222-2222-2222-2222-222222222222
另一个人
主街2号
222222222222
33333333-3333-3333-3333-333333333333
一些
人
111111111111
...
这是我当前的实现:

公共字符串替换为FalseData(字符串xmlInstance){
文档dom=toDOM(xmlInstance);
XPathExpression=XPathExpressionFactory.createXPathExpression(“//Person”);
List nodeList=xPathExpression.evaluatesNodeList(dom);
for(节点人员节点:节点列表){
Map childNodes=getChildNodes(personNode);
字符串personID=childNodes.get(“personID”).getTextContent();
//使用该ID检索缓存的假用户,如果不存在,则创建一个新用户。
Person fakePerson=getFakePerson(personID);
setIfExists(childNodes.get(“firstName”)、fakePerson.getFirstName();
setIfExists(childNodes.get(“lastName”)、fakePerson.getLastName();
setIfExists(childNodes.get(“地址”),fakePerson.getAddress();
setIfExists(childNodes.get(“personID”)、fakePerson.getPersonID();
}
返回到字符串(dom);
}
公共映射getChildNodes(节点父节点){
Map childNodes=new HashMap();
对于(节点child=parent.getFirstChild();child!=null;child=child.getNextSibling()){
if(child.getLocalName()!=null){
put(child.getLocalName(),child);
}
}
返回子节点;
}
public void setIfExists(节点,字符串值){
如果(节点!=null){
node.setTextContent(值);
}
}

您正在使用基于DOM的API。使用用于XML的流式API(StAX)可以实现更快的替换,在许多情况下,它的性能优于基于DOM的API:

domapi比StAX占用更多内存,这会降低性能,但比staxapi更易于使用

您的示例的工作解决方案-在150 MB xml文件上测试,10秒内替换:

import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.FileWriter;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import javax.xml.stream.XMLEventFactory;
import javax.xml.stream.XMLEventReader;
import javax.xml.stream.XMLEventWriter;
import javax.xml.stream.XMLInputFactory;
import javax.xml.stream.XMLOutputFactory;
import javax.xml.stream.XMLStreamException;
import javax.xml.stream.events.XMLEvent;


public class ReplaceXmlWithFakeUser
{
  public static void main(String[] args) throws XMLStreamException, IOException
  {
    XMLInputFactory inFactory = XMLInputFactory.newInstance();
    XMLEventReader eventReader = inFactory.createXMLEventReader(new BufferedInputStream(new FileInputStream("c:\\temp\\persons.xml")));
    XMLOutputFactory factory = XMLOutputFactory.newInstance();
    XMLEventWriter writer = factory.createXMLEventWriter(new BufferedOutputStream(new FileOutputStream("c:\\temp\\fakePersons.xml")));
    XMLEventFactory eventFactory = XMLEventFactory.newInstance();
    while (eventReader.hasNext())
    {
      XMLEvent event = eventReader.nextEvent();

      if (event.getEventType() == XMLEvent.START_ELEMENT &&
        event.asStartElement().getName().toString().equals("Person"))
      {
        //write Person startElement:
        writer.add(event);


        /*
        STEP 1:
        personId is at the end of Person element. Cannot overwrite firstName and address element with fake data yet. Must call getFakePerson() first.
        Iterate till you read Person END element and just remember all events within person element which we will overwrite with fake data in step 2.
         */
        Person fakePerson=null;

        List<XMLEvent> eventsWithinPersonElement = new ArrayList<XMLEvent>();

        event = eventReader.nextEvent();
        while(!(event.getEventType() == XMLEvent.END_ELEMENT && event.asEndElement().getName().toString().equals("Person")))
        {

          eventsWithinPersonElement.add(event);

          if(event.getEventType() == XMLEvent.START_ELEMENT &&
              event.asStartElement().getName().toString().equals("personID"))
          {
            XMLEvent personIDContentEvent = eventReader.nextEvent();

            String personId = personIDContentEvent.asCharacters().toString();
            fakePerson = getFakePerson(personId);

            eventsWithinPersonElement.add(personIDContentEvent);
          }

          event = eventReader.nextEvent();
        }
        XMLEvent personEndElement=event;


        //STEP 2:
        for (Iterator<XMLEvent> eventWithinPersonElementIterator = eventsWithinPersonElement.iterator(); eventWithinPersonElementIterator.hasNext(); )
        {
          XMLEvent eventWithinPersonElement = eventWithinPersonElementIterator.next();

          writer.add(eventWithinPersonElement);

          if(eventWithinPersonElement.getEventType() == XMLEvent.START_ELEMENT &&
              eventWithinPersonElement.asStartElement().getName().toString().equals("personID"))
          {
            writer.add(eventFactory.createCharacters(fakePerson.personId));

            //skip personId event
            eventWithinPersonElementIterator.next();
          }
          if(eventWithinPersonElement.getEventType() == XMLEvent.START_ELEMENT &&
              eventWithinPersonElement.asStartElement().getName().toString().equals("firstName"))
          {
            writer.add(eventFactory.createCharacters(fakePerson.firstName));

            //skip real firstName
            eventWithinPersonElementIterator.next();
          }
          if(eventWithinPersonElement.getEventType() == XMLEvent.START_ELEMENT &&
              eventWithinPersonElement.asStartElement().getName().toString().equals("lastName"))
          {
            writer.add(eventFactory.createCharacters(fakePerson.lastName));

            //skip real firstName
            eventWithinPersonElementIterator.next();
          }
          else if(eventWithinPersonElement.getEventType() == XMLEvent.START_ELEMENT &&
              eventWithinPersonElement.asStartElement().getName().toString().equals("address"))
          {
            writer.add(eventFactory.createCharacters(fakePerson.address));

            //skip real address
            eventWithinPersonElementIterator.next();

          }
        }

        writer.add(personEndElement);
      }
      else
      {
        writer.add(event);
      }
    }
    writer.close();
  }

  private static Person getFakePerson(String personId)
  {
    //create simple fake user...

    Person fakePerson = new Person();
    fakePerson.personId = personId;
    fakePerson.firstName = "fake first name: " + Math.random();
    fakePerson.lastName = "fake last name: " + Math.random();
    fakePerson.address = "fake address: " + Math.random();

    return fakePerson;
  }

  static class Person
  {
    String personId;
    String firstName;
    String lastName;
    String address;

  }
}
生成此
fakePersons.xml
结果:

<?xml version="1.0" encoding="UTF-8"?><ADocument>
    <Stuff>
        <StuffA></StuffA>
    </Stuff>
    <OtherStuff>
        <OtherStuff>
            <ABC>yada yada</ABC>
        </OtherStuff>
    </OtherStuff>

    <Person>
        <uuid>11111111-1111-1111-1111-111111111111</uuid>
        <firstName>fake first name: 0.9518514637129984</firstName>
        <lastName>fake last name: 0.3495378044884426</lastName>
        <personID>111111111111</personID>
    </Person>
    <Person>
        <uuid>22222222-2222-2222-2222-222222222222</uuid>
        <firstName>fake first name: 0.8945739434355868</firstName>
        <address>fake address: 0.40784763231471777</address>
        <personID>222222222222</personID>
    </Person>
    <Person>
        <uuid>33333333-3333-3333-3333-333333333333</uuid>
        <firstName>fake first name: 0.7863207851479257</firstName>
        <lastName>fake last name: 0.09918620445731652</lastName>
        <personID>111111111111</personID>
    </Person>

    <MoreStuff>
        <foo></foo>
        <foo>fooo</foo>
        <foo><bar></bar></foo>
        <foo>
            <bar></bar>
            <bar></bar>
            <bar>bb</bar>
        </foo>
        <bar></bar>
    </MoreStuff>

</ADocument>

雅达雅达
11111111-1111-1111-1111-111111111111
假名字:0.9518514637129984
假姓:0.3495378044884426
111111111111
22222222-2222-2222-2222-222222222222
假名字:0.8945739434355868
假地址:0.40784763231471777
222222222222
33333333-3333-3333-3333-333333333333
假名字:0.7863207851479257
假姓:0.09918620445731652
111111111111
福奥
bb

我不确定XSLT是否能帮上忙。也许我对XSLT的了解还不够深入,但XSLT用于基于现有XML的数据创建新的XML结构。在这里,您似乎想要做相反的事情:维护相同的结构,但基于动态值更新数据。您可能很难创建这样的XSLT。 优化可能取决于相当多的参数:每个XML中Person元素的数量、XML中相等PersonID的数量、要处理的XML的数量。。。如果要处理大文件,可能需要切换到SAX实现以优化内存消耗。如果在同一个XML中重新分配大量相同的PersonID,则可以在用于替换的伪数据后面构建一些缓存结构,以减少对DOM的点击量(可以直接用缓存节点替换节点,并用原始节点覆盖uuid)。 如果有很多小文件包含类似的Personid,如果可以接受相同的伪数据可以在多个XML文件上使用,那么您可能希望使用跨XML缓存


另外,我相信您可以在PersonID上删除“setIfExists”,因为它是一个必填字段。

我不能对相对性能发表评论,但这里有一个XSLT解决方案

以下XSLT样式表:

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
  <xsl:output method="xml" indent="yes" omit-xml-declaration="yes"/>

  <!-- Find the position of the first Person with the same personID as this Person. 
       This will be used to provide a unique identifier for that person. -->
  <xsl:template name="get-position-id">
    <xsl:value-of select="count(../../Person[personID=current()/../personID][1]/preceding-sibling::Person)"/>
  </xsl:template>

  <!-- For personID elements, we will replace the number with a number based on the position of 
       the first Person with the same personId. -->
  <xsl:template match="personID">
    <xsl:copy>
      <xsl:variable name="position-id">
        <xsl:call-template name="get-position-id"/>
      </xsl:variable>
      <xsl:call-template name="create-person-id">
        <xsl:with-param name="input" select="$position-id"/>
      </xsl:call-template>
    </xsl:copy>
  </xsl:template>

  <!-- For elements that match this template, we will replace the text with an arbitrary string
       appended with a number linking them to a particular personID. -->
  <xsl:template match="firstName|lastName|address">
    <xsl:copy>
      <xsl:variable name="position-id">
        <xsl:call-template name="get-position-id"/>
      </xsl:variable>
      <xsl:call-template name="create-fake-string">
        <xsl:with-param name="input" select="$position-id"/>
      </xsl:call-template>
    </xsl:copy>    
  </xsl:template>

  <!-- The identity transform. -->
  <xsl:template match="@*|node()">
    <xsl:copy>
      <xsl:apply-templates select="@*|node()"/>
    </xsl:copy>
  </xsl:template>

  <!-- This template generates a number that can be used to replace personID. -->
  <xsl:template name="create-person-id">
    <xsl:param name="input"/>
    <!-- Turn the input into a 12-digit number padded by zeroes. -->
    <xsl:value-of select="format-number($input, '000000000000') "/>
  </xsl:template>

  <!-- This template generates a string that can be used to replace data. -->
  <xsl:template name="create-fake-string">
    <xsl:param name="input"/>
    <!-- Create a string to replace data with, appending the input parameter. -->
    <xsl:text>FAKEDATA</xsl:text>
    <xsl:value-of select="$input"/>
  </xsl:template>

</xsl:stylesheet>

伪造数据
应用于示例文档时生成以下XML:

<ADocument>
  <Stuff>
    ...
  </Stuff>
  <OtherStuff>
    ...
  </OtherStuff>
  <Person>
    <uuid>11111111-1111-1111-1111-111111111111</uuid>
    <firstName>FAKEDATA0</firstName>
    <lastName>FAKEDATA0</lastName>
    <personID>000000000000</personID>
  </Person>
  <Person>
    <uuid>22222222-2222-2222-2222-222222222222</uuid>
    <firstName>FAKEDATA1</firstName>
    <address>FAKEDATA1</address>
    <personID>000000000001</personID>
  </Person>
  <Person>
    <uuid>33333333-3333-3333-3333-333333333333</uuid>
    <firstName>FAKEDATA0</firstName>
    <lastName>FAKEDATA0</lastName>
    <personID>000000000000</personID>
  </Person>
  <MoreStuff>
    ...
  </MoreStuff>
</ADocument>

...
...
11111111-1111-1111-1111-111111111111
伪造数据0
伪造数据0
000000000000
22222222-2222-2222-2222-222222222222
伪造数据1
伪造数据1
000000000001
33333333-3333-3333-3333-333333333333
伪造数据0
伪造数据0
000000000000
...

感谢所有贡献者!我使用DOM实现、Sergej的StAX实现、Ben的XSLT实现以及我自己的另一个实现usin对一组2000个XML文档进行了性能测试