Can I re-order an existing XML to adhere to an XSD

后端 未结 2 1633
终归单人心
终归单人心 2021-01-06 13:34

We\'re generating an XML with Java (org.w3c.dom.Node), using essentially

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

this gen

相关标签:
2条回答
  • 2021-01-06 14:14

    I've done this before by traversing the schema and then pulling relevant pieces from the XML model and streaming it along the way.

    There are multiple xsd model libraries to use:

    • xsom
    • xerces
    • xmlschema

    Here's an example using xsom (which can be replaced by one of the above) and xom (which can be replaced with dom)

    Main:

    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)));
        }
      }
    }
    

    The actual reordering logic. You will probably have to modify this further. For example, I didn't have to deal with the xsd:any for my project.

    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) {}
    }
    

    Stax writer:

    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);
        }
      }
    }
    

    The xml view:

    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 implementation:

    Document:

    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());
      }
    }
    

    Element:

    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();
          }
        };
      }
    }
    

    Attribute:

    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();
      }
    }
    
    0 讨论(0)
  • 2021-01-06 14:15

    I don't think something like that exists. Simple reordering might be possible, but XML-Schema allows to define so many constraints, that it might be impossible to write a general tool that converts some XML document into a valid document according to some schema.

    So, just build the document correctly in the first place.

    0 讨论(0)
提交回复
热议问题