当前位置: 首页 > 知识库问答 >
问题:

XML节点值的快速替换

王长卿
2023-03-14

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

  • UUID-必填项,不应触及。
  • 名字-可选
  • LastName-可选
  • 地址-可选
  • 个人身份-必需

一个人可能会出现很多次,在这种情况下应该使用相同的假数据,即如果两个person节点具有相同的personID,那么它们都应该接收到相同的假ID。

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

下面是一个示例文档:

<ADocument>
  <Stuff>
    ...
  </Stuff>
  <OtherStuff>
    ...
  </OtherStuff>
  <Person>
    <uuid>11111111-1111-1111-1111-111111111111</uuid>
    <firstName>Some</firstName>
    <lastName>Person</lastName>
    <personID>111111111111</personID>
  </Person>
  <Person>
    <uuid>22222222-2222-2222-2222-222222222222</uuid>
    <firstName>Another Person</firstName>
    <address>Main St. 2</address>
    <personID>222222222222</personID>
  </Person>
  <Person>
    <uuid>33333333-3333-3333-3333-333333333333</uuid>
    <firstName>Some</firstName>
    <lastName>Person</lastName>
    <personID>111111111111</personID>
  </Person>
  <MoreStuff>
    ...
  </MoreStuff>
</ADocument>

这是我当前的实现:

public String replaceWithFalseData(String xmlInstance) {
    Document dom = toDOM(xmlInstance);

    XPathExpression xPathExpression = XPathExpressionFactory.createXPathExpression("//Person");
    List<Node> nodeList = xPathExpression.evaluateAsNodeList(dom);

    for(Node personNode : nodeList) {
        Map<String, Node> childNodes = getChildNodes(personNode);
        String personID = childNodes.get("personID").getTextContent();
        // Retrieve a cached fake person using the ID, or create a new one if none exists.
        Person fakePerson = getFakePerson(personID);

        setIfExists(childNodes.get("firstName"), fakePerson.getFirstName());
        setIfExists(childNodes.get("lastName"), fakePerson.getLastName());
        setIfExists(childNodes.get("address"), fakePerson.getAddress());
        setIfExists(childNodes.get("personID"), fakePerson.getPersonID());
    }

    return toString(dom);
}

public Map<String, Node> getChildNodes(Node parent) {
    Map<String, Node> childNodes = new HashMap<String, Node>();
    for(Node child = parent.getFirstChild(); child != null; child = child.getNextSibling()) {
        if(child.getLocalName() != null) {
            childNodes.put(child.getLocalName(), child);
        }
    }
    return childNodes;
}

public void setIfExists(Node node, String value) {
    if(node != null) {
        node.setTextContent(value);
    }
}

共有2个答案

海叶秋
2023-03-14

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

此外,我相信您可以删除PersonID上的“set ifexists”,因为它被声明为强制字段。

邵星河
2023-03-14

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

DOM API比StAX占用更多的内存,这会降低性能,但比StAX API更容易使用。

您的示例的工作解决方案-在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;

  }
}

使用persons.xml作为输入:

<ADocument>
    <Stuff>
        <StuffA></StuffA>
    </Stuff>
    <OtherStuff>
        <OtherStuff>
            <ABC>yada yada</ABC>
        </OtherStuff>
    </OtherStuff>

    <Person>
        <uuid>11111111-1111-1111-1111-111111111111</uuid>
        <firstName>Some</firstName>
        <lastName>Person</lastName>
        <personID>111111111111</personID>
    </Person>
    <Person>
        <uuid>22222222-2222-2222-2222-222222222222</uuid>
        <firstName>Another Person</firstName>
        <address>Main St. 2</address>
        <personID>222222222222</personID>
    </Person>
    <Person>
        <uuid>33333333-3333-3333-3333-333333333333</uuid>
        <firstName>Some</firstName>
        <lastName>Person</lastName>
        <personID>111111111111</personID>
    </Person>

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

</ADocument>

生成此fakepers.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>
 类似资料:
  • 在本章中,我们将学习XML DOM对象中的替换节点操作。DOM中的所有内容都保存在分层信息单元中,替换节点提供了另一种更新这些指定节点或文本节点的方法。 以下是替换节点的两个方法 - 1. replaceChild()方法 方法用新节点替换指定的节点。 语法 具有以下语法 - 其中, - 是放入子列表的新节点。 - 是列表中要替换的节点。 此方法返回已替换的节点。 示例 以下示例(replacen

  • 下面是我的XSL文件的内容: 当我运行我的程序时,输出与原始XML相同。f_name和l_name节点都没有重命名,Version节点值也没有被替换。 任何帮助都将不胜感激。

  • 问题内容: 这是我的代码,用于将字节数据转换为浮点数。我尝试了本网站给出的所有答案。我正在获取此“ <44fa0000>”字节数据的指数值 谢谢 问题答案: 是二进制浮点数的 big-endian 内存表示形式。要从数据中获取数字,您必须先将其读入,从big-endian转换为主机字节序,然后将结果转换为a 。 在 Swift 2 中 例: 在 Swift 3中, 您可以使用代替,而 可以用 初始

  • 我有一个方法可以创建一个带有节点和关系的图。我想将节点ID列表返回给调用方,以便后面的步骤可以快速定位这些节点并创建其他关系。我将无法返回节点实例。 在SQL世界中,我可以返回主键或唯一键。在neo4j的世界里,我应该回报什么?是否有一个唯一的节点标识符,我可以使用,而不会注定自己的地狱第七级?我有自己的身份证吗?由于我沉迷于SQL,我在学习Neo4J方法时遇到了困难。

  • 问题内容: 我正在使用节点v0.10.26并表示v4.2.0,而我对节点还很新。在过去三个多小时的时间里,我一直在想着要在节点上使用文件上传表单,这是我的头。在这一点上,我只是试图让req.files不返回未定义。我的观点看起来像这样 这是我的路线 这是我的app.js 我看到了其中包含的内容,应该可以提供帮助,但是如果添加这些行,我什至无法启动服务器。 问题答案: ExpressJS问题: 大多

  • 问题内容: 我正在尝试读取xml文件,例如: 这是我到目前为止的代码: 这是我尝试编写此代码的尝试,怎么说都不成功,这就是我开始赏金的原因。这是http://pastebin.com/huKP4KED。 赏金更新: 我确实真的尝试了好几天,但现在没想到会这么难,我会接受有用的链接/书籍/教程,但更喜欢代码,因为我昨天需要这样做。 这是我需要的: 关于上面的xml: 我需要获取标题ID的值 temp