首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >如何在java中解析无效XML时的XMP?

如何在java中解析无效XML时的XMP?
EN

Stack Overflow用户
提问于 2012-12-06 06:38:37
回答 1查看 2.9K关注 0票数 0

我正在使用javax.imageio从PNG图像中提取元数据。这可以很好地工作。但是用于获取实际元数据的getAsTree方法返回的XML是无效的。所以我不知道如何解析这个XML来获得特定的元数据:

代码语言:javascript
复制
run:
Format name: javax_imageio_png_1.0
<javax_imageio_png_1.0>
    <IHDR width="256" height="256" bitDepth="8" colorType="RGBAlpha" compressionMethod="deflate" filterMethod="adaptive" interlaceMethod="none"/>
    <cHRM whitePointX="31269" whitePointY="32899" redX="63999" redY="33001" greenX="30000" greenY="60000" blueX="15000" blueY="5999"/>
    <gAMA value="45454"/>
    <iTXt>
        <iTXtEntry keyword="XML:com.adobe.xmp" compressionFlag="FALSE" compressionMethod="0" languageTag="" translatedKeyword="" text="<?xpacket begin="" id="W5M0MpCehiHzreSzNTczkc9d"?>
<x:xmpmeta xmlns:x="adobe:ns:meta/" x:xmptk="Adobe XMP Core 5.0-c061 64.140949, 2010/12/07-10:57:01        ">
 <rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#">
  <rdf:Description rdf:about=""
    xmlns:xmp="http://ns.adobe.com/xap/1.0/"
    xmlns:xmpMM="http://ns.adobe.com/xap/1.0/mm/"
    xmlns:stEvt="http://ns.adobe.com/xap/1.0/sType/ResourceEvent#"
    xmlns:lr="http://ns.adobe.com/lightroom/1.0/"
    xmlns:dc="http://purl.org/dc/elements/1.1/"
   xmp:MetadataDate="2012-12-05T21:36:19+01:00"
   xmpMM:InstanceID="xmp.iid:EF7F11740720681192B08F682498C71D"
   xmpMM:DocumentID="xmp.did:FC7F11740720681192B0AE5890E66CAE"
   xmpMM:OriginalDocumentID="xmp.did:FC7F11740720681192B0AE5890E66CAE">
   <xmpMM:History>
    <rdf:Seq>
     <rdf:li
      stEvt:action="saved"
      stEvt:instanceID="xmp.iid:FC7F11740720681192B0AE5890E66CAE"
      stEvt:when="2012-12-04T00:23:34+01:00"
      stEvt:changed="/metadata"/>
     <rdf:li
      stEvt:action="saved"
      stEvt:instanceID="xmp.iid:EF7F11740720681192B08F682498C71D"
      stEvt:when="2012-12-05T21:36:19+01:00"
      stEvt:changed="/metadata"/>
    </rdf:Seq>
   </xmpMM:History>
   <lr:hierarchicalSubject>
    <rdf:Bag>
     <rdf:li>Component|Software</rdf:li>
     <rdf:li>Places|Paris</rdf:li>
     <rdf:li>Product|Christensen</rdf:li>
     <rdf:li>Product|Simba</rdf:li>
    </rdf:Bag>
   </lr:hierarchicalSubject>
   <dc:subject>
    <rdf:Bag>
     <rdf:li>Christensen</rdf:li>
     <rdf:li>Paris</rdf:li>
     <rdf:li>Simba</rdf:li>
     <rdf:li>Software</rdf:li>
    </rdf:Bag>
   </dc:subject>
  </rdf:Description>
 </rdf:RDF>
</x:xmpmeta>
<?xpacket end="r"?>"/>
    </iTXt>
    <pHYs pixelsPerUnitXAxis="2835" pixelsPerUnitYAxis="2835" unitSpecifier="meter"/>
</javax_imageio_png_1.0>
Format name: javax_imageio_1.0
<javax_imageio_1.0>
    <Chroma>
        <ColorSpaceType name="RGB"/>
        <NumChannels value="4"/>
        <Gamma value="0.45453998"/>
        <BlackIsZero value="TRUE"/>
    </Chroma>
    <Compression>
        <CompressionTypeName value="deflate"/>
        <Lossless value="TRUE"/>
        <NumProgressiveScans value="1"/>
    </Compression>
    <Data>
        <PlanarConfiguration value="PixelInterleaved"/>
        <SampleFormat value="UnsignedIntegral"/>
        <BitsPerSample value="8 8 8 8"/>
    </Data>
    <Dimension>
        <PixelAspectRatio value="1.0"/>
        <ImageOrientation value="Normal"/>
        <HorizontalPixelSize value="0.35273367"/>
        <VerticalPixelSize value="0.35273367"/>
    </Dimension>
    <Text>
        <TextEntry keyword="XML:com.adobe.xmp" value="<?xpacket begin="" id="W5M0MpCehiHzreSzNTczkc9d"?>
<x:xmpmeta xmlns:x="adobe:ns:meta/" x:xmptk="Adobe XMP Core 5.0-c061 64.140949, 2010/12/07-10:57:01        ">
 <rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#">
  <rdf:Description rdf:about=""
    xmlns:xmp="http://ns.adobe.com/xap/1.0/"
    xmlns:xmpMM="http://ns.adobe.com/xap/1.0/mm/"
    xmlns:stEvt="http://ns.adobe.com/xap/1.0/sType/ResourceEvent#"
    xmlns:lr="http://ns.adobe.com/lightroom/1.0/"
    xmlns:dc="http://purl.org/dc/elements/1.1/"
   xmp:MetadataDate="2012-12-05T21:36:19+01:00"
   xmpMM:InstanceID="xmp.iid:EF7F11740720681192B08F682498C71D"
   xmpMM:DocumentID="xmp.did:FC7F11740720681192B0AE5890E66CAE"
   xmpMM:OriginalDocumentID="xmp.did:FC7F11740720681192B0AE5890E66CAE">
   <xmpMM:History>
    <rdf:Seq>
     <rdf:li
      stEvt:action="saved"
      stEvt:instanceID="xmp.iid:FC7F11740720681192B0AE5890E66CAE"
      stEvt:when="2012-12-04T00:23:34+01:00"
      stEvt:changed="/metadata"/>
     <rdf:li
      stEvt:action="saved"
      stEvt:instanceID="xmp.iid:EF7F11740720681192B08F682498C71D"
      stEvt:when="2012-12-05T21:36:19+01:00"
      stEvt:changed="/metadata"/>
    </rdf:Seq>
   </xmpMM:History>
   <lr:hierarchicalSubject>
    <rdf:Bag>
     <rdf:li>Component|Software</rdf:li>
     <rdf:li>Places|Paris</rdf:li>
     <rdf:li>Product|Christensen</rdf:li>
     <rdf:li>Product|Simba</rdf:li>
    </rdf:Bag>
   </lr:hierarchicalSubject>
   <dc:subject>
    <rdf:Bag>
     <rdf:li>Christensen</rdf:li>
     <rdf:li>Paris</rdf:li>
     <rdf:li>Simba</rdf:li>
     <rdf:li>Software</rdf:li>
    </rdf:Bag>
   </dc:subject>
  </rdf:Description>
 </rdf:RDF>
</x:xmpmeta>
<?xpacket end="r"?>" language="" compression="none"/>
    </Text>
    <Transparency>
        <Alpha value="nonpremultipled"/>
    </Transparency>
</javax_imageio_1.0>
BUILD SUCCESSFUL (total time: 3 seconds)

无效的XML从iTXtEntry元素开始,该元素具有xpact位并包含子元素,尽管它具有自结束标记格式,而不是结束标记。因此,当我尝试使用DOM文档和xpath解析它时,我得到一个错误,指出该元素的内容中不能包含">“。

我已经在DocumentBuilderFactory上禁用了DTD验证。这没什么用。我觉得我只能使用正则表达式了,但这似乎不太对。为什么我首先从imageio中的getAsTree方法获得无效的XML,我能做些什么呢?

EN

回答 1

Stack Overflow用户

回答已采纳

发布于 2012-12-07 00:51:16

您的问题是无意义的,因为IIOMetaData.getAsTree()返回一个DOM Node对象,该对象是节点树的根。这是XML在内存中的表示。它不是从任何地方解析的,所以它不可能是无效的。xml文档字符串可能是无效的,但是这里没有要解析的字符串。getAsTree方法直接在内存中创建XML。

问题出在您的输出生成了无效的XML。无论是什么从getAsTree()序列化你的节点,都是不正确的。也就是说,它没有正确地转义text属性的值,该属性本身就是一个XML文档字符串。

下面是一个完整的示例,演示了如何获取图像元数据并将其序列化为(有效) XML字符串。

代码语言:javascript
复制
import java.io.*;
import java.util.*;

// for imageio metadata
import javax.imageio.*;
import javax.imageio.stream.*;
import javax.imageio.metadata.*;

// for xml handling
import org.w3c.dom.*;
import javax.xml.transform.*;
import javax.xml.transform.dom.*;
import javax.xml.transform.stream.*;

public class imgmeta {
    // Very lazy exception handling
    // This is just a quick example
    public static void main(String[] args) throws Exception {
        String filename = args[0];

        File file = new File(filename);
        ImageInputStream imagestream = ImageIO.createImageInputStream(file);

        // get a reader which is able to read this file
        Iterator<ImageReader> readers = ImageIO.getImageReaders(imagestream);
        ImageReader reader = readers.next();

        // feed image to reader
        reader.setInput(imagestream, true);

        // get metadata of first image
        IIOMetadata metadata = reader.getImageMetadata(0);

        // get any metadata format name
        // (you should prefer the native one, but not all images have one)
        // String mdataname = metadata.getNativeMetadataFormatName(); // might be null
        String[] mdatanames = metadata.getMetadataFormatNames();

        String mdataname = mdatanames[0];

        Node metadatadom = metadata.getAsTree(mdataname);

        // metadatadom is now a DOM Node root of a DOM tree
        // representing metadata in the image
        // Since it's in-memory, it can't be "invalid"
        // because it's already been parsed


        // now let's serialize to an XML string
        // javax.xml.transform.Transformer takes xml sources
        // in one representation and transforms them to xml
        // in another representation
        // Representations include: DOM, JAXB, SAX, stream, etc
        DOMSource source = new DOMSource(metadatadom);

        StringWriter writer = new StringWriter();
        StreamResult result = new StreamResult(writer);

        Transformer transformer = TransformerFactory.newInstance().newTransformer();
        transformer.transform(source, result);

        // THIS is what you want:
        String metadata_in_xml = writer.toString();

        // now print it:
        System.out.print(metadata_in_xml);
    }
}

这是使用我周围的图像运行的测试输出:

代码语言:javascript
复制
$ java imgtest testimage.png | xmllint --format -
<?xml version="1.0" encoding="UTF-8"?>
<javax_imageio_png_1.0>
  <IHDR width="149" height="237" bitDepth="8" colorType="RGBAlpha" compressionMethod="deflate" filterMethod="adaptive" interlaceMethod="none"/>
  <iTXt>
    <iTXtEntry keyword="XML:com.adobe.xmp" compressionFlag="0" compressionMethod="0" languageTag="" translatedKeyword="" text="&lt;?xpacket begin=&quot;?&quot; id=&quot;W5M0MpCehiHzreSzNTczkc9d&quot;?&gt; &lt;x:xmpmeta xmlns:x=&quot;adobe:ns:meta/&quot; x:xmptk=&quot;Adobe XMP Core 5.0-c061 64.140949, 2010/12/07-10:57:01        &quot;&gt; &lt;rdf:RDF xmlns:rdf=&quot;http://www.w3.org/1999/02/22-rdf-syntax-ns#&quot;&gt; &lt;rdf:Description rdf:about=&quot;&quot; xmlns:xmp=&quot;http://ns.adobe.com/xap/1.0/&quot; xmlns:xmpMM=&quot;http://ns.adobe.com/xap/1.0/mm/&quot; xmlns:stRef=&quot;http://ns.adobe.com/xap/1.0/sType/ResourceRef#&quot; xmp:CreatorTool=&quot;Adobe Photoshop CS5.1 Macintosh&quot; xmpMM:InstanceID=&quot;xmp.iid:D281E43D34DC11E2BFE69DA1E5D17E5F&quot; xmpMM:DocumentID=&quot;xmp.did:D281E43E34DC11E2BFE69DA1E5D17E5F&quot;&gt; &lt;xmpMM:DerivedFrom stRef:instanceID=&quot;xmp.iid:D281E43B34DC11E2BFE69DA1E5D17E5F&quot; stRef:documentID=&quot;xmp.did:D281E43C34DC11E2BFE69DA1E5D17E5F&quot;/&gt; &lt;/rdf:Description&gt; &lt;/rdf:RDF&gt; &lt;/x:xmpmeta&gt; &lt;?xpacket end=&quot;r&quot;?&gt;"/>
  </iTXt>
  <tEXt>
    <tEXtEntry keyword="Software" value="Adobe ImageReady"/>
  </tEXt>
</javax_imageio_png_1.0>

生成的XML是有效的:

代码语言:javascript
复制
$ java imgmeta testimage.png | xmllint --noout -
$

(无输出表示有效。)

注意iTXtEntrytext属性值是如何转义的。如果想要检索该属性中的数据,则需要检索字符串,然后将其解析为自己的keyword="XML:com.adobe.xmp"文档,以获得另一个DOM (或其他任何)树。

更新:解析XMP数据

下面是一些示例代码,演示了如何提取属性值,并在XML和DOM树之间进行解析。

代码语言:javascript
复制
public class XMPExample {
public static String transformXML(Node xml) throws Exception {
    StringWriter writer = new StringWriter();

    Transformer transformer = TransformerFactory.newInstance().newTransformer();
    transformer.transform(new DOMSource(xml), new StreamResult(writer));

    return writer.toString();
}

public static Document transformXML(String xml) throws Exception {
    StringReader reader = new StringReader(xml);
    Document doc = DocumentBuilderFactory.newInstance().newDocumentBuilder().newDocument();
    Transformer transformer = TransformerFactory.newInstance().newTransformer();

    transformer.transform(new StreamSource(reader), new DOMResult(doc));
    return doc;
}

public static String getXMP(Element metadata_dom) throws Exception {
            // (Element) type because getElementsByTagName() method is required

    // There are many more robust ways of selecting nodes
    // (e.g. javax.xml.xpath), but this is for a simple example
    // that only uses the native DOM methods

    // This is very brittle because we're making assumptions about
    // the metadata_dom structure. There are two sources of brittleness:

    // 1. The metadata format from `metadata.getMetadataFormatNames()`.
    //    You should probably settle on a standard one you know will
    //    exist, like 'javax_imageio_1.0'
    // 2. How the image stores the metadata. Usually XMP data will
    //    be in a text field with keyword 'XML:com.adobe.xmp', but
    //    I don't know that this is *always* the case.

    // the code below assumes "javax_imageio_png_1.0" format
    NodeList iTXtEntries = metadata_dom.getElementsByTagName("iTXtEntry");
    Element iTXtEntry = null;
    Element entry = null;
    for (int i = 0; i < iTXtEntries.getLength(); i++) {
        entry = (Element) iTXtEntries.item(i);
        if (entry.getAttribute("keyword").equals("XML:com.adobe.xmp")) {
            iTXtEntry = entry;
            break;
        }
    }
    if (iTXtEntry == null) {
        return null;
    }

    String xmp_xml_doc = iTXtEntry.getAttribute("text");

    return xmp_xml_doc;

}
}
代码语言:javascript
复制
// Use like so:
Node metadatanode = metadata.getAsTree(metadataname);

String xmp_xml = XMPExample.getXMP((Element) metadatanode);

// xmp_xml is now an xml document STRING
System.out.print(xmp_xml);

// If you want to parse it as an XML document, use an XML parser.
Document xmp_dom = XMPExample.transformXML(xmp_xml);

// ...and you can serialize it again when you are done.
String xmp_xml_roundtripped = XMPExample.transformXML(xmp_dom);
票数 3
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/13733787

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档