【问题标题】:Can I re-order an existing XML to adhere to an XSD我可以重新排序现有的 XML 以遵守 XSD
【发布时间】:2013-03-14 18:27:42
【问题描述】:

我们使用 Java (org.w3c.dom.Node) 生成 XML,本质上使用

parent.appendChild(doc.createElement(nodeName));

这会生成一个 XML,其中节点按调用“appendChild”的顺序排序。然而,最终的 XML 需要遵守给定的 XSD。我们的代码可以确保有效的值类型、必填字段等都可以。然而,我正在努力处理节点顺序。

是否有任何方法:

  • 在插入时确保节点顺序与 XSD 匹配
  • 根据 XSD 创建后对整个 XML 重新排序

澄清一下:

我们拥有的是:

<myNodeA>...</myNodeA>
<myNodeC>...</myNodeC>
<myNodeB>...</myNodeB>

XSD 想要的是:

<myNodeA>...</myNodeA>
<myNodeB>...</myNodeB>
<myNodeC>...</myNodeC>

谢谢,西蒙

【问题讨论】:

  • 您正在遍历节点名称以执行 parent.appendChild(doc.createElement(nodeName));称呼。在调用 appendChild 之前订购要循环的集合

标签: java xml xsd


【解决方案1】:

我之前通过遍历架构然后从 XML 模型中提取相关部分并沿途对其进行流式传输来完成此操作。

有多个 xsd 模型库可供使用:

  • xsom
  • 练习
  • xmlschema

这是一个使用 xsom(可以替换为上述之一)和 xom(可以替换为 dom)的示例

主要:

package main;

import java.io.File;
import java.io.FileOutputStream;
import java.io.OutputStream;

import javax.xml.stream.XMLOutputFactory;
import javax.xml.stream.XMLStreamWriter;

import node.xom.WrappedDocument;
import nu.xom.Builder;
import nu.xom.Document;
import nu.xom.Element;
import reorder.xsom.UncheckedXMLStreamWriter;
import reorder.xsom.XSVisitorWriteOrdered;

import com.sun.xml.xsom.XSElementDecl;
import com.sun.xml.xsom.XSSchemaSet;
import com.sun.xml.xsom.parser.XSOMParser;

public class ReorderXmlToXsd {
  public static void main(String[] args) throws Exception {
    File unorderedXml = new File("unordered.xml");
    File xsd = new File("your.xsd");
    File orderedXml = new File("ordered.xml");

    XSOMParser p = new XSOMParser();
    p.parse(xsd);
    XSSchemaSet parsed = p.getResult();

    Builder xom = new Builder();
    Document unorderedDoc = xom.build(unorderedXml);
    Element unorderedRoot = unorderedDoc.getRootElement();

    XSElementDecl root = parsed.getElementDecl(
        unorderedRoot.getNamespaceURI(),
        unorderedRoot.getLocalName());

    XMLOutputFactory stax = XMLOutputFactory.newInstance();

    try (OutputStream to = new FileOutputStream(orderedXml)) {
      XMLStreamWriter using = stax.createXMLStreamWriter(to, "UTF-8");

      root.visit(
          new XSVisitorWriteOrdered(
              new WrappedDocument(unorderedDoc),
              new UncheckedXMLStreamWriter(using)));
    }
  }
}

实际的重新排序逻辑。您可能需要进一步修改它。例如,我不必为我的项目处理 xsd:any。

package reorder.xsom;

import node.WrappedNode;

import com.sun.xml.xsom.*;
import com.sun.xml.xsom.visitor.XSVisitor;

public class XSVisitorWriteOrdered implements XSVisitor {
  private final WrappedNode currNode;
  private final UncheckedXMLStreamWriter writeTo;

  public XSVisitorWriteOrdered(WrappedNode currNode, UncheckedXMLStreamWriter writeTo) {
    this.currNode = currNode;
    this.writeTo = writeTo;
  }

  @Override
  public void attributeUse(XSAttributeUse use) {
    attributeDecl(use.getDecl());
  }

  @Override
  public void modelGroupDecl(XSModelGroupDecl decl) {
    modelGroup(decl.getModelGroup());
  }

  @Override
  public void modelGroup(XSModelGroup model) {
    for (XSParticle term : model.getChildren()) {
      term.visit(this);
    }
  }

  @Override
  public void particle(XSParticle particle) {
    XSTerm term = particle.getTerm();
    term.visit(this);
  }

  @Override
  public void complexType(XSComplexType complex) {
    for (XSAttributeUse use : complex.getAttributeUses()) {
      attributeUse(use);
    }

    XSContentType contentType = complex.getContentType();
    contentType.visit(this);
  }

  @Override
  public void elementDecl(XSElementDecl decl) {
    String namespaceUri = decl.getTargetNamespace();
    String localName = decl.getName();

    for (WrappedNode child : currNode.getChildElements(namespaceUri, localName)) {
      writeTo.writeStartElement(namespaceUri, localName);

      XSType type = decl.getType();
      type.visit(new XSVisitorWriteOrdered(child, writeTo));

      writeTo.writeEndElement();
    }
  }

  @Override
  public void attributeDecl(XSAttributeDecl decl) {
    String namespaceUri = decl.getTargetNamespace();
    String localName = decl.getName();

    WrappedNode attribute = currNode.getAttribute(namespaceUri, localName);
    if (attribute != null) {
      String value = attribute.getValue();
      if (value != null) {
        writeTo.writeAttribute(namespaceUri, localName, value);
      }
    }
  }

  @Override
  public void simpleType(XSSimpleType simpleType) {
    String value = currNode.getValue();
    if (value != null) {
      writeTo.writeCharacters(value);
    }
  }

  @Override
  public void empty(XSContentType empty) {}

  @Override
  public void facet(XSFacet facet) {}

  @Override
  public void annotation(XSAnnotation ann) {}

  @Override
  public void schema(XSSchema schema) {}

  @Override
  public void notation(XSNotation notation) {}

  @Override
  public void identityConstraint(XSIdentityConstraint decl) {}

  @Override
  public void xpath(XSXPath xp) {}

  @Override
  public void wildcard(XSWildcard wc) {}

  @Override
  public void attGroupDecl(XSAttGroupDecl decl) {}
}

税务作家:

package reorder.xsom;

import javax.xml.stream.XMLStreamException;
import javax.xml.stream.XMLStreamWriter;

public class UncheckedXMLStreamWriter {
  private final XMLStreamWriter real;

  public UncheckedXMLStreamWriter(XMLStreamWriter delegate) {
    this.real = delegate;
  }

  public void writeStartElement(String namespaceUri, String localName) {
    try {
      real.writeStartElement(namespaceUri, localName);
    } catch (XMLStreamException e) {
      throw new RuntimeException(e);
    }
  }

  public void writeEndElement() {
    try {
      real.writeEndElement();
    } catch (XMLStreamException e) {
      throw new RuntimeException(e);
    }
  }

  public  void writeAttribute(String namespaceUri, String localName, String value) {
    try {
      real.writeAttribute(namespaceUri, localName, value);
    } catch (XMLStreamException e) {
      throw new RuntimeException(e);
    }
  }

  public void writeCharacters(String value) {
    try {
      real.writeCharacters(value);
    } catch (XMLStreamException e) {
      throw new RuntimeException(e);
    }
  }
}

xml 视图:

package node;

import java.util.List;

import javax.annotation.Nullable;

public interface WrappedNode {
  List<? extends WrappedNode> getChildElements(String namespaceUri, String localName);

  @Nullable
  WrappedNode getAttribute(String namespaceUri, String localName);

  @Nullable
  String getValue();
}

XOM 实现:

文档:

package node.xom;

import java.util.Collections;
import java.util.List;

import node.WrappedNode;
import nu.xom.Document;
import nu.xom.Element;

public class WrappedDocument implements WrappedNode {
  private final Document d;

  public WrappedDocument(Document d) {
    this.d = d;
  }

  @Override
  public List<WrappedElement> getChildElements(String namespaceUri, String localName) {
    Element root = d.getRootElement();
    if (isAt(root, namespaceUri, localName)) {
      return Collections.singletonList(new WrappedElement(root));
    }
    return Collections.emptyList();
  }

  @Override
  public WrappedAttribute getAttribute(String namespaceUri, String localName) {
    throw new UnsupportedOperationException();
  }

  @Override
  public String getValue() {
    throw new UnsupportedOperationException();
  }

  @Override
  public String toString() {
    return d.toString();
  }

  private static boolean isAt(Element e, String namespaceUri, String localName) {
    return namespaceUri.equals(e.getNamespaceURI())
        && localName.equals(e.getLocalName());
  }
}

元素:

package node.xom;

import java.util.AbstractList;
import java.util.List;

import node.WrappedNode;
import nu.xom.Attribute;
import nu.xom.Element;
import nu.xom.Elements;

class WrappedElement implements WrappedNode {
  private final Element e;

  WrappedElement(Element e) {
    this.e = e;
  }

  @Override
  public List<WrappedElement> getChildElements(String namespaceUri, String localName) {
    return asList(e.getChildElements(localName, namespaceUri));
  }

  @Override
  public WrappedAttribute getAttribute(String namespaceUri, String localName) {
    Attribute attribute = e.getAttribute(localName, namespaceUri);
    return (attribute != null) ? new WrappedAttribute(attribute) : null;
  }

  @Override
  public String getValue() {
    return e.getValue();
  }

  @Override
  public String toString() {
    return e.toString();
  }

  private static List<WrappedElement> asList(final Elements eles) {
    return new AbstractList<WrappedElement>() {
      @Override
      public WrappedElement get(int index) {
        return new WrappedElement(eles.get(index));
      }

      @Override
      public int size() {
        return eles.size();
      }
    };
  }
}

属性:

package node.xom;

import java.util.List;

import node.WrappedNode;
import nu.xom.Attribute;

class WrappedAttribute implements WrappedNode {
  private final Attribute a;

  WrappedAttribute(Attribute a) {
    this.a = a;
  }

  @Override
  public List<WrappedNode> getChildElements(String namespaceUri, String localName) {
    throw new UnsupportedOperationException();
  }

  @Override
  public WrappedNode getAttribute(String namespaceUri, String localName) {
    throw new UnsupportedOperationException();
  }

  @Override
  public String getValue() {
    return a.getValue();
  }

  @Override
  public String toString() {
    return a.toString();
  }
}

【讨论】:

  • 这看起来像是一项荒谬的工作,只是为了解决你一开始就可以轻松完成的事情......但是,也许它解决了一些我不知道的问题......跨度>
  • 哈哈,确实。这样做是因为输入 xsd/xml 事先不知道,所以清理必须动态完成。
  • @slow:这看起来和我一直在寻找的完全一样。我将首先检查我们的代码是否真的无法生成符合 XSD 的 XML。但如果没有,我会尝试上面的方法。谢谢!
  • 我不得不花费一些时间来使它适用于命名空间。首先添加stax.setProperty(XMLOutputFactory.IS_REPAIRING_NAMESPACES, true); 然后在UncheckedXMLStreamWriter.writeAttribute 我必须检测一个空的命名空间,如果是,那么让它继承元素命名空间。 if ("".equals(namespaceUri)) { real.writeAttribute(localName, value); } else { real.writeAttribute(namespaceUri, localName, value); }。抱歉格式错误。感谢您的出色实施非常有帮助。
【解决方案2】:

我认为不存在这样的事情。简单的重新排序可能是可能的,但 XML-Schema 允许定义如此多的约束,以至于不可能编写一个通用的工具来根据一些模式将一些 XML 文档转换为有效的文档。

所以,首先要正确构建文档。

【讨论】:

  • 我知道所有可能的限制......但我真正关心的是重新排序。
猜你喜欢
  • 2023-03-13
  • 2014-10-07
  • 2013-10-03
  • 2019-02-05
  • 1970-01-01
  • 1970-01-01
  • 2014-09-20
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多