В Java, как я могу разобрать XML-схему (xsd), чтобы узнать, что является допустимым в данном элементе?

Я хотел бы иметь возможность читать в XML-схеме (то есть xsd) и, исходя из этого, знать, что являются действительными атрибутами, дочерними элементами, значениями, когда я прохожу ее.

Например, допустим, у меня есть xsd, который будет проверять этот xml:

<root>
  <element-a type="something">
    <element-b>blah</element-b>
    <element-c>blahblah</element-c>
  </element-a>
</root>

Я возился с несколькими библиотеками и могу с уверенностью получить <root> в качестве корневого элемента. Помимо этого я потерян.

Учитывая элемент, мне нужно знать, какие дочерние элементы необходимы или разрешены, атрибуты, фасеты, выбор и т. Д. Используя приведенный выше пример, я бы хотел знать, что element-a имеет атрибут type и может иметь детей element-b а также element-c... или должны иметь детей element-b а также element-c... или, должно быть, по одному на каждого... ты получаешь картинку, я надеюсь.

Я просмотрел многочисленные библиотеки, такие как XSOM, Eclipse XSD, Apache XmlSchema, и обнаружил, что у них все не хватает хорошего примера кода. Мой поиск в Интернете также не увенчался успехом.

Кто-нибудь знает хороший пример или даже книгу, которая демонстрирует, как пройти через схему XML и выяснить, какие будут допустимые параметры в данной точке в проверенном документе XML?

осветление

Я не собираюсь проверять документ, скорее я хотел бы знать параметры в данный момент, чтобы помочь в создании или редактировании документа. Если я знаю "я здесь" в документе, я бы хотел определить, что я могу сделать в этот момент. Msgstr "Вставить один из элементов A, B или C" или "прикрепить атрибут 'description'".

6 ответов

Решение

Многие из решений для проверки XML в java используют JAXB API. Здесь доступно обширное руководство. Основной рецепт для выполнения того, что вы ищете с JAXB, заключается в следующем:

  1. Получите или создайте схему XML для проверки.
  2. Генерация Java-классов для привязки XML к использованию xjc Компилятор JAXB
  3. Написать код Java для:
    1. Откройте содержимое XML в качестве входного потока.
    2. Создать JAXBContext а также Unmarshaller
    3. Передайте входной поток в Unmarshaller "s unmarshal метод.

Части урока, которые вы можете прочитать для этого:

  1. Привет, мир
  2. Unmarshalling XML

Это хороший вопрос. Хотя, он старый, я не нашел приемлемого ответа. Дело в том, что существующие библиотеки, о которых я знаю ( XSOM, Apache XmlSchema), спроектированы как объектные модели. Разработчики не собирались предоставлять какие-либо служебные методы - вам следует рассмотреть возможность их реализации самостоятельно с использованием предоставленной объектной модели.

Давайте посмотрим, как запросить контекстно-зависимые элементы можно с помощью Apache XmlSchema.

Вы можете использовать их учебник в качестве отправной точки. Кроме того, платформа Apache CFX предоставляет классу XmlSchemaUtils множество полезных примеров кода.

Прежде всего, прочитайте XmlSchemaCollection как показано в руководстве по библиотеке:

XmlSchemaCollection xmlSchemaCollection = new XmlSchemaCollection();
xmlSchemaCollection.read(inputSource, new ValidationEventHandler());

Теперь XML-схема определяет два типа типов данных:

  • Простые типы
  • Сложные типы

Простые типы представлены XmlSchemaSimpleType учебный класс. Обращаться с ними легко. Прочитайте документацию: https://ws.apache.org/commons/XmlSchema/apidocs/org/apache/ws/commons/schema/XmlSchemaSimpleType.html. Но давайте посмотрим, как обрабатывать сложные типы. Давайте начнем с простого метода:

@Override
public List<QName> getChildElementNames(QName parentElementName) {
    XmlSchemaElement element = xmlSchemaCollection.getElementByQName(parentElementName);
    XmlSchemaType type = element != null ? element.getSchemaType() : null;

    List<QName> result = new LinkedList<>();
    if (type instanceof XmlSchemaComplexType) {
        addElementNames(result, (XmlSchemaComplexType) type);
    }
    return result;
}

XmlSchemaComplexType может стоять как для реального типа, так и для extension элемент. Пожалуйста, смотрите public static QName getBaseType(XmlSchemaComplexType type) метод XmlSchemaUtils учебный класс.

private void addElementNames(List<QName> result, XmlSchemaComplexType type) {
    XmlSchemaComplexType baseType = getBaseType(type);
    XmlSchemaParticle particle = baseType != null ? baseType.getParticle() : type.getParticle();

    addElementNames(result, particle);
}

Когда вы справляетесь XmlSchemaParticle Учтите, что он может иметь несколько реализаций. См.: https://ws.apache.org/commons/XmlSchema/apidocs/org/apache/ws/commons/schema/XmlSchemaParticle.html

private void addElementNames(List<QName> result, XmlSchemaParticle particle) {
    if (particle instanceof XmlSchemaAny) {

    } else if (particle instanceof XmlSchemaElement) {

    } else if (particle instanceof XmlSchemaGroupBase) {

    } else if (particle instanceof XmlSchemaGroupRef) {

    }
}

Другая вещь, которую нужно иметь в виду, состоит в том, что элементы могут быть абстрактными или конкретными. Опять же, JavaDocs являются лучшим руководством.

Это довольно полный пример того, как анализировать XSD с помощью XSOM:

import java.io.File;
import java.util.Iterator;
import java.util.Vector;

import org.xml.sax.ErrorHandler;

import com.sun.xml.xsom.XSComplexType;
import com.sun.xml.xsom.XSElementDecl;
import com.sun.xml.xsom.XSFacet;
import com.sun.xml.xsom.XSModelGroup;
import com.sun.xml.xsom.XSModelGroupDecl;
import com.sun.xml.xsom.XSParticle;
import com.sun.xml.xsom.XSRestrictionSimpleType;
import com.sun.xml.xsom.XSSchema;
import com.sun.xml.xsom.XSSchemaSet;
import com.sun.xml.xsom.XSSimpleType;
import com.sun.xml.xsom.XSTerm;
import com.sun.xml.xsom.impl.Const;
import com.sun.xml.xsom.parser.XSOMParser;
import com.sun.xml.xsom.util.DomAnnotationParserFactory;

public class XSOMNavigator
{
    public static class SimpleTypeRestriction
    {
        public String[] enumeration = null;
        public String   maxValue    = null;
        public String   minValue    = null;
        public String   length      = null;
        public String   maxLength   = null;
        public String   minLength   = null;
        public String[] pattern     = null;
        public String   totalDigits = null;
        public String   fractionDigits = null;
        public String   whiteSpace = null;

        public String toString()
        {
            String enumValues = "";
            if (enumeration != null)
            {
                for(String val : enumeration)
                {
                    enumValues += val + ", ";
                }
                enumValues = enumValues.substring(0, enumValues.lastIndexOf(','));
            }

            String patternValues = "";
            if (pattern != null)
            {
                for(String val : pattern)
                {
                    patternValues += "(" + val + ")|";
                }
                patternValues = patternValues.substring(0, patternValues.lastIndexOf('|'));
            }
            String retval = "";
            retval += minValue    == null ? "" : "[MinValue  = "   + minValue      + "]\t";
            retval += maxValue    == null ? "" : "[MaxValue  = "   + maxValue      + "]\t";
            retval += minLength   == null ? "" : "[MinLength = "   + minLength     + "]\t";
            retval += maxLength   == null ? "" : "[MaxLength = "   + maxLength     + "]\t";
            retval += pattern     == null ? "" : "[Pattern(s) = "  + patternValues + "]\t";
            retval += totalDigits == null ? "" : "[TotalDigits = " + totalDigits   + "]\t";
            retval += fractionDigits == null ? "" : "[FractionDigits = " + fractionDigits   + "]\t";
            retval += whiteSpace  == null ? "" : "[WhiteSpace = "      + whiteSpace        + "]\t";          
            retval += length      == null ? "" : "[Length = "      + length        + "]\t";          
            retval += enumeration == null ? "" : "[Enumeration Values = "      + enumValues    + "]\t";

            return retval;
        }
    }

    private static void initRestrictions(XSSimpleType xsSimpleType, SimpleTypeRestriction simpleTypeRestriction)
    {
        XSRestrictionSimpleType restriction = xsSimpleType.asRestriction();
        if (restriction != null)
        {
            Vector<String> enumeration = new Vector<String>();
            Vector<String> pattern     = new Vector<String>();

            for (XSFacet facet : restriction.getDeclaredFacets())
            {
                if (facet.getName().equals(XSFacet.FACET_ENUMERATION))
                {
                    enumeration.add(facet.getValue().value);
                }
                if (facet.getName().equals(XSFacet.FACET_MAXINCLUSIVE))
                {
                    simpleTypeRestriction.maxValue = facet.getValue().value;
                }
                if (facet.getName().equals(XSFacet.FACET_MININCLUSIVE))
                {
                    simpleTypeRestriction.minValue = facet.getValue().value;
                }
                if (facet.getName().equals(XSFacet.FACET_MAXEXCLUSIVE))
                {
                    simpleTypeRestriction.maxValue = String.valueOf(Integer.parseInt(facet.getValue().value) - 1);
                }
                if (facet.getName().equals(XSFacet.FACET_MINEXCLUSIVE))
                {
                    simpleTypeRestriction.minValue = String.valueOf(Integer.parseInt(facet.getValue().value) + 1);
                }
                if (facet.getName().equals(XSFacet.FACET_LENGTH))
                {
                    simpleTypeRestriction.length = facet.getValue().value;
                }
                if (facet.getName().equals(XSFacet.FACET_MAXLENGTH))
                {
                    simpleTypeRestriction.maxLength = facet.getValue().value;
                }
                if (facet.getName().equals(XSFacet.FACET_MINLENGTH))
                {
                    simpleTypeRestriction.minLength = facet.getValue().value;
                }
                if (facet.getName().equals(XSFacet.FACET_PATTERN))
                {
                    pattern.add(facet.getValue().value);
                }
                if (facet.getName().equals(XSFacet.FACET_TOTALDIGITS))
                {
                    simpleTypeRestriction.totalDigits = facet.getValue().value;
                }
                if (facet.getName().equals(XSFacet.FACET_FRACTIONDIGITS))
                {
                    simpleTypeRestriction.fractionDigits = facet.getValue().value;
                }
                if (facet.getName().equals(XSFacet.FACET_WHITESPACE))
                {
                    simpleTypeRestriction.whiteSpace = facet.getValue().value;
                }
            }
            if (enumeration.size() > 0)
            {
                simpleTypeRestriction.enumeration = enumeration.toArray(new String[] {});
            }
            if (pattern.size() > 0)
            {
                simpleTypeRestriction.pattern = pattern.toArray(new String[] {});
            }
        }
    }

    private static void printParticle(XSParticle particle, String occurs, String absPath, String indent)
    {
        boolean repeats = particle.isRepeated();
        occurs = "  MinOccurs = " + particle.getMinOccurs() + ", MaxOccurs = " + particle.getMaxOccurs() + ", Repeats = " + Boolean.toString(repeats);
        XSTerm term = particle.getTerm();
        if (term.isModelGroup())
        {
            printGroup(term.asModelGroup(), occurs, absPath, indent);    
        }
        else if(term.isModelGroupDecl())
        {
            printGroupDecl(term.asModelGroupDecl(), occurs, absPath, indent);    
        }
        else if (term.isElementDecl())
        {
            printElement(term.asElementDecl(), occurs, absPath, indent);
        }
    }

    private static void printGroup(XSModelGroup modelGroup, String occurs, String absPath, String indent)
    {
        System.out.println(indent + "[Start of Group " + modelGroup.getCompositor() + occurs + "]" );
        for (XSParticle particle : modelGroup.getChildren())
        {
            printParticle(particle, occurs, absPath, indent + "\t");
        }
        System.out.println(indent + "[End of Group " + modelGroup.getCompositor() + "]");
    }

    private static void printGroupDecl(XSModelGroupDecl modelGroupDecl, String occurs, String absPath, String indent)
    {
        System.out.println(indent + "[GroupDecl " + modelGroupDecl.getName() + occurs + "]");
        printGroup(modelGroupDecl.getModelGroup(), occurs, absPath, indent);
    }

    private static void printComplexType(XSComplexType complexType, String occurs, String absPath, String indent)
    {
        System.out.println();
        XSParticle particle = complexType.getContentType().asParticle();
        if (particle != null)
        {
            printParticle(particle, occurs, absPath, indent);
        }
    }

    private static void printSimpleType(XSSimpleType simpleType, String occurs, String absPath, String indent)
    {
        SimpleTypeRestriction restriction = new SimpleTypeRestriction();
        initRestrictions(simpleType, restriction);
        System.out.println(restriction.toString());
    }

    public static void printElement(XSElementDecl element, String occurs, String absPath, String indent)
    {
        absPath += "/" + element.getName();
        String typeName = element.getType().getBaseType().getName();
        if(element.getType().isSimpleType() && element.getType().asSimpleType().isPrimitive())
        {
            // We have a primitive type - So use that instead
            typeName = element.getType().asSimpleType().getPrimitiveType().getName();
        }

        boolean nillable = element.isNillable();
        System.out.print(indent + "[Element " + absPath + "   " + occurs + "] of type [" + typeName + "]" + (nillable ? " [nillable] " : ""));
        if (element.getType().isComplexType())
        {
            printComplexType(element.getType().asComplexType(), occurs, absPath, indent);
        }
        else
        {
            printSimpleType(element.getType().asSimpleType(), occurs, absPath, indent);
        }
    }

    public static void printNameSpace(XSSchema s, String indent)
    {
        String nameSpace = s.getTargetNamespace();

        // We do not want the default XSD namespaces or a namespace with nothing in it
        if(nameSpace == null || Const.schemaNamespace.equals(nameSpace) || s.getElementDecls().isEmpty())
        {
            return;
        }

        System.out.println("Target namespace: " + nameSpace);
        Iterator<XSElementDecl> jtr = s.iterateElementDecls();
        while (jtr.hasNext())
        {
            XSElementDecl e = (XSElementDecl) jtr.next();

            String occurs  = "";
            String absPath = "";

            XSOMNavigator.printElement(e, occurs, absPath,indent);
            System.out.println();
        }
    }

    public static void xsomNavigate(File xsdFile)
    {
        ErrorHandler    errorHandler    = new ErrorReporter(System.err);
        XSSchemaSet     schemaSet = null;

        XSOMParser parser = new XSOMParser();
        try
        {
            parser.setErrorHandler(errorHandler);
            parser.setAnnotationParser(new DomAnnotationParserFactory());
            parser.parse(xsdFile);
            schemaSet = parser.getResult();
        }
        catch (Exception exp)
        {
            exp.printStackTrace(System.out);
        }

        if(schemaSet != null)
        {
            // iterate each XSSchema object. XSSchema is a per-namespace schema.
            Iterator<XSSchema> itr = schemaSet.iterateSchema();
            while (itr.hasNext())
            {
                XSSchema s = (XSSchema) itr.next();
                String indent  = "";
                printNameSpace(s, indent);
            }
        }
    }

    public static void printFile(String fileName)
    {
        File fileToParse = new File(fileName);
        if (fileToParse != null && fileToParse.canRead())
        {
            xsomNavigate(fileToParse);
        }
    }
}

А для вашего Error Reporter используйте:

import java.io.OutputStream;
import java.io.PrintStream;
import java.text.MessageFormat;

import org.xml.sax.ErrorHandler;
import org.xml.sax.SAXException;
import org.xml.sax.SAXParseException;

public class ErrorReporter implements ErrorHandler {

    private final PrintStream out;

    public ErrorReporter( PrintStream o ) { this.out = o; }
    public ErrorReporter( OutputStream o ) { this(new PrintStream(o)); }

    public void warning(SAXParseException e) throws SAXException {
        print("[Warning]",e);
    }

    public void error(SAXParseException e) throws SAXException {
        print("[Error  ]",e);
    }

    public void fatalError(SAXParseException e) throws SAXException {
        print("[Fatal  ]",e);
    }

    private void print( String header, SAXParseException e ) {
        out.println(header+' '+e.getMessage());
        out.println(MessageFormat.format("   line {0} at {1}",
            new Object[]{
                Integer.toString(e.getLineNumber()),
                e.getSystemId()}));
    }
}

Для вашего основного использования:

открытый класс WDXSOMParser {

    public static void main(String[] args)
    {
        String fileName = null;
        if(args != null && args.length > 0 && args[0] != null)
            fileName = args[0];
        else
        fileName = "C:\\xml\\CollectionComments\\CollectionComment1.07.xsd";
        //fileName = "C:\\xml\\PropertyListingContractSaleInfo\\PropertyListingContractSaleInfo.xsd";
        //fileName = "C:\\xml\\PropertyPreservation\\PropertyPreservation.xsd";

        XSOMNavigator.printFile(fileName);
    }
}

Я вижу, вы пробовали Eclipse XSD. Вы пробовали Eclipse Modeling Framework (EMF)? Вы можете:

Генерация модели EMF с использованием XML-схемы (XSD)

Создайте динамический экземпляр из вашей метамодели (3.1 С помощью инструмента создания динамического экземпляра)

Это для изучения XSD. Вы можете создать динамический экземпляр корневого элемента, затем вы можете щелкнуть правой кнопкой мыши по элементу и создать дочерний элемент. Там вы увидите, что такое возможный дочерний элемент и так далее.

Что касается сохранения созданной модели EMF в xsd, соответствующий xml: я должен поискать его. Я думаю, что вы можете использовать JAXB для этого ( Как использовать EMF для чтения XML-файла?).


Некоторые ссылки:

EMF: Eclipse Modeling Framework, 2-е издание (написано создателями)
Eclipse Modeling Framework (EMF)
Познакомьтесь с Eclipse Modeling Framework (EMF) и его динамическими возможностями
Создание динамических моделей EMF из XSD и загрузка их экземпляров из XML в виде SDO

Это хорошая работа, в зависимости от того, насколько сложен ваш xsd, но в основном.

если у тебя есть

<Document>
<Header/>
<Body/>
<Document>

И вы хотели выяснить, где находятся допустимые дочерние элементы заголовка (с учетом пространств имен), в Xpath вам нужно искать '/element[name="Document"]/element[name="Header"]'

После этого это зависит от того, сколько вы хотите сделать. Возможно, вам будет проще написать или найти что-то, что загружает xsd в структуру типа DOM. Конечно, вы можете найти все виды вещей под этим элементом в xsd, выбор, последовательность, любые, атрибуты, complexType, SimpleContent, аннотации.

Много времени, требующего веселья.

Другие вопросы по тегам