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

Я хотел бы иметь возможность читать в 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?

уточнение

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

Ответы [ 6 ]

4 голосов
/ 27 ноября 2011

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

  1. Получите или создайте схему XML для проверки.
  2. Создайте классы Java, чтобы связать XML сиспользуя xjc, компилятор JAXB.
  3. Записать код Java в:
    1. Открыть содержимое XML в качестве входного потока.
    2. Создать JAXBContext и Unmarshaller
    3. Передайте входной поток методу Unmarshaller unmarshal.

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

  1. Привет, мир
  2. Неустрашимый XML
3 голосов
/ 25 декабря 2014

Это хороший вопрос.Хотя, он старый, я не нашел приемлемого ответа.Дело в том, что существующие библиотеки, о которых я знаю ( 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 являются лучшим руководством.

1 голос
/ 27 ноября 2011

Я вижу, вы пробовали 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

0 голосов
/ 08 мая 2016

Это довольно полный пример того, как анализировать 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);
    }
}
0 голосов
/ 01 ноября 2012
0 голосов
/ 27 ноября 2011

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

если у вас было

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

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

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

Грузы, отнимающие много времени.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...