如何使用JAXB在Java中将CSV转换为XML

如何使用JAXB在Java中将CSV转换为XML

本文介绍了如何使用JAXB在Java中将CSV转换为XML的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正尝试使用apache骆驼使用CSV文件,并将每一行转换为XML,然后将每个xml发布为可安慰主题. XML格式很复杂,我不确定如何在Java中创建如此复杂的XML. CSV包含员工有效负载字段. XML的其他部分(包括标头,auditRecords和ancillaryData)都是硬编码/静态数据(时间戳可以是当前日期).

I am trying to consume a CSV file using apache camel and convert each row to XML and publish each xml to solace topic. XML format is complex and I am not sure how to create such a complex XML in Java. CSV contains employee payload fields. Other part of XML including header, auditRecords and ancillaryData are hardcoded/static data(timestamp can be current date).

如何使用JAXB在Java中将CSV转换为XML?

How to convert CSV to XML in Java using JAXB?

<canonMessage xmlns="http://www.test.com/canon/v1">
   <header>
      <metadata>
         <domain>
            <name>party</name>
            <schemaVersion>1.0</schemaVersion>
            <subdomain>
               <name>employee</name>
            </subdomain>
         </domain>
         <identifier>
            <id idScheme="HR/lanId">kabcde</id>
         </identifier>
         <source>HR</source>
         <messageId>352247</messageId>
         <version>v1234520171106</version>
      </metadata>
   </header>
   <payload>
      <employee xmlns="http://www.testcompany.com/party/employee/v1" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.testcompany.com/party/employee/v1 employee-v1.xsd">
         <employeeId>a</employeeId>
         <lanId>a</lanId>
         <name>
            <lastName>a</lastName>
            <firstName>a</firstName>
         </name>
         <businessTitle>String</businessTitle>
         <status>String</status>
         <legalEntity>String</legalEntity>
         <groupName>String</groupName>
         <divisionName>String</divisionName>
         <departmentName>String</departmentName>
         <costCentre>String</costCentre>
         <officeLocation>String</officeLocation>
         <region>String</region>
         <citizenship>
            <citizenshipCountry>String</citizenshipCountry>
            <citizenshipCountry>String</citizenshipCountry>
         </citizenship>
      </employee>
   </payload>
   <auditRecords>
      <sourceAuditRecord>
         <system>PeopleSoftHR</system>
         <user />
         <timestamp>
            <created>2016-09-29T09:41:54.436+08:00</created>
            <updated>2016-09-29T09:41:54.436+08:00</updated>
            <sent>2016-09-29T09:42:48.366+08:00</sent>
         </timestamp>
      </sourceAuditRecord>
      <auditRecord>
         <system>listener-settlement</system>
         <timestamp>
            <received>2016-09-29T11:44:56.306+10:00</received>
         </timestamp>
      </auditRecord>
   </auditRecords>
   <ancillaryData>
      <dataClassification xmlns="http://www.test.com/data-classification/v1">
         <compartments>
            <compartment name="domain">employee</compartment>
            <compartment name="confidentiality">confidential</compartment>
         </compartments>
      </dataClassification>
   </ancillaryData>
   <trailer>
      <checksum type="sha-256">df29183132b0733e5afbe5a9ab44f74ee7b43fff4d48daa</checksum>
   </trailer>
</canonMessage>

员工类别:

@XmlRootElement
@XmlAccessorType(XmlAccessType.FIELD)
@CsvRecord(separator = ",")
public class Employee {

    @DataField(pos = 1)
    @XmlElement
    public String employeeId;

    @DataField(pos = 2)
    @XmlElement
    public String lanId;

    @DataField(pos = 3)
    @XmlElement
    public String status;

    @DataField(pos = 4)
    @XmlElement
    public String costCentre;

    @DataField(pos = 5)
    @XmlElement
    public String groupName;

    @DataField(pos = 6)
    @XmlElement
    public String divisionName;

    @DataField(pos = 7)
    @XmlElement
    public String departmentName;

    @DataField(pos = 8)
    @XmlElement
    public String region;

    @DataField(pos = 9)
    @XmlElement
    public String businessTitle;

    @DataField(pos = 10)
    @XmlElement
    public String officeLocation;

    @DataField(pos = 11)
    @XmlElement
    public String legalEntity;

    @DataField(pos = 12)
    @XmlElement
    public String birthDate;

    @DataField(pos = 13)
    @XmlElement
    public String firstName;

    @DataField(pos = 14)
    @XmlElement
    public String lastName;

    public String getEmployeeId() {
        return employeeId;
    }

    public void setEmployeeId(String employeeId) {
        this.employeeId = employeeId;
    }

    public String getLanId() {
        return lanId;
    }

    public void setLanId(String lanId) {
        this.lanId = lanId;
    }

    public String getStatus() {
        return status;
    }

    public void setStatus(String status) {
        this.status = status;
    }

    public String getCostCentre() {
        return costCentre;
    }

    public void setCostCentre(String costCentre) {
        this.costCentre = costCentre;
    }

    public String getGroupName() {
        return groupName;
    }

    public void setGroupName(String groupName) {
        this.groupName = groupName;
    }

    public String getDivisionName() {
        return divisionName;
    }

    public void setDivisionName(String divisionName) {
        this.divisionName = divisionName;
    }

    public String getDepartmentName() {
        return departmentName;
    }

    public void setDepartmentName(String departmentName) {
        this.departmentName = departmentName;
    }

    public String getRegion() {
        return region;
    }

    public void setRegion(String region) {
        this.region = region;
    }

    public String getBusinessTitle() {
        return businessTitle;
    }

    public void setBusinessTitle(String businessTitle) {
        this.businessTitle = businessTitle;
    }

    public String getOfficeLocation() {
        return officeLocation;
    }

    public void setOfficeLocation(String officeLocation) {
        this.officeLocation = officeLocation;
    }

    public String getLegalEntity() {
        return legalEntity;
    }

    public void setLegalEntity(String legalEntity) {
        this.legalEntity = legalEntity;
    }

    public String getBirthDate() {
        return birthDate;
    }

    public void setBirthDate(String birthDate) {
        this.birthDate = birthDate;
    }

    public String getFirstName() {
        return firstName;
    }

    public void setFirstName(String firstName) {
        this.firstName = firstName;
    }

    public String getLastName() {
        return lastName;
    }

    public void setLastName(String lastName) {
        this.lastName = lastName;
    }

}

标题类:

import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlElementWrapper;
import javax.xml.bind.annotation.XmlRootElement;

@XmlRootElement(name="metadata")
@XmlAccessorType(XmlAccessType.FIELD)
public class Header {

    @XmlElementWrapper(name = "domain")
    private List<Domain> domain;

    private String id;

    private String source;

    private String messageId;

    private String version;

    public List<Domain> getDomain() {
        return domain;
    }

    public void setDomain(List<Domain> domain) {
        this.domain = domain;
    }

    public String getId() {
        return id;
    }

    public void setId(String id) {
        this.id = id;
    }

    public String getSource() {
        return source;
    }

    public void setSource(String source) {
        this.source = source;
    }

    public String getMessageId() {
        return messageId;
    }

    public void setMessageId(String messageId) {
        this.messageId = messageId;
    }

    public String getVersion() {
        return version;
    }

    public void setVersion(String version) {
        this.version = version;
    }

    public Header() {

    }

    public Header(List<Domain> domain, String id, String source, String messageId, String version) {
        this.domain = domain;
        this.id = id;
        this.source = source;
        this.messageId = messageId;
        this.version = version;
    }
}

域类:

@XmlRootElement(name="domain")
@XmlAccessorType(XmlAccessType.FIELD)
public class Domain {

    private String name;

    private String schemaVersion;

    //@XmlElementWrapper(name = "subdomain")
    private String subdomain;

    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public String getSchemaVersion() {
        return schemaVersion;
    }
    public void setSchemaVersion(String schemaVersion) {
        this.schemaVersion = schemaVersion;
    }
    public String getSubdomain() {
        return subdomain;
    }
    public void setSubdomain(String subdomain) {
        this.subdomain = subdomain;
    }

    public Domain() {

    }

    public Domain(String name, String schemaVersion, String subdomain) {
        this.name = name;
        this.schemaVersion = schemaVersion;
        this.subdomain = subdomain;
    }
}

服务:

import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBException;
import javax.xml.bind.Marshaller;

import org.springframework.stereotype.Service;


@Service
public class HrCanonicalService {

    private final String BOOKSTORE_XML = "app/hrci-files/bookstore-jaxb.xml";
    public void process(Employee employee) {

        JAXBContext context;
        try {
            context = JAXBContext.newInstance(CanonMessage.class);
            Marshaller m = context.createMarshaller();
            m.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, Boolean.TRUE);

            // Write to System.out
            m.marshal(employee, System.out);

            // Write to File
            m.marshal(employee, new File(BOOKSTORE_XML));
        } catch (JAXBException e) {
            e.printStackTrace();
        }

    }
}

推荐答案

我设法根据上述XML模式创建了XML.我跳过了ancillaryData和auditRecord元素,因为它不是必需的.让我知道这是否是形成XML的正确方法.

I managed to create the XML based on the above XML schema.I skipped ancillaryData and auditRecord elements as it was not mandatory.Let me know if this is the correct way of forming the XML.

CanonMessage.java

CanonMessage.java

import java.util.List;

import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlElementWrapper;
import javax.xml.bind.annotation.XmlRootElement;

@XmlRootElement(name="canonMessage")
@XmlAccessorType(XmlAccessType.FIELD)
public class CanonMessage {

    @XmlElementWrapper(name = "header")
    @XmlElement(name = "metadata")
    private List<Header> metadata;

    @XmlElementWrapper(name = "payload")
    @XmlElement(name = "employee")
    private List<Employee> employee;

    @XmlElementWrapper(name = "auditRecords")
    @XmlElement(name = "sourceAuditRecord")
    private List<SourceAuditRecord> sourceAuditRecord;

    @XmlElementWrapper(name = "trailer")
    @XmlElement(name = "checksum")
    private List<Trailer> trailer;

    public List<Header> getMetadata() {
        return metadata;
    }

    public void setMetadata(List<Header> metadata) {
        this.metadata = metadata;
    }

    public List<Employee> getEmployee() {
        return employee;
    }

    public void setEmployee(List<Employee> employee) {
        this.employee = employee;
    }

    public List<SourceAuditRecord> getSourceAuditRecord() {
        return sourceAuditRecord;
    }

    public void setSourceAuditRecord(List<SourceAuditRecord> sourceAuditRecord) {
        this.sourceAuditRecord = sourceAuditRecord;
    }

    public List<Trailer> getTrailer() {
        return trailer;
    }

    public void setTrailer(List<Trailer> trailer) {
        this.trailer = trailer;
    }

}

Header.java

Header.java

import java.util.List;

import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlElementWrapper;
import javax.xml.bind.annotation.XmlRootElement;

@XmlRootElement(name="metadata")
@XmlAccessorType(XmlAccessType.FIELD)
public class Header {

    private List<Domain> domain;

    @XmlElementWrapper(name = "identifier")
    @XmlElement(name = "id")
    private List<Identifier> identifier;

    private String source;

    private String messageId;

    public List<Domain> getDomain() {
        return domain;
    }

    public void setDomain(List<Domain> domain) {
        this.domain = domain;
    }

    public List<Identifier> getIdentifier() {
        return identifier;
    }

    public void setIdentifier(List<Identifier> identifier) {
        this.identifier = identifier;
    }

    public String getSource() {
        return source;
    }

    public void setSource(String source) {
        this.source = source;
    }

    public String getMessageId() {
        return messageId;
    }

    public void setMessageId(String messageId) {
        this.messageId = messageId;
    }

    public Header() {

    }

    public Header(List<Domain> domain, List<Identifier> identifier, String source, String messageId) {
        this.domain = domain;
        this.identifier = identifier;
        this.source = source;
        this.messageId = messageId;

    }
}

Identifier.java

Identifier.java

import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlAttribute;
import javax.xml.bind.annotation.XmlRootElement;
import javax.xml.bind.annotation.XmlValue;

@XmlRootElement(name = "id")
@XmlAccessorType(XmlAccessType.FIELD)
public class Identifier {

    @XmlAttribute(name = "idScheme")
    private String id;

    @XmlValue
    private String Value;

    public String getId() {
        return id;
    }

    public void setId(String id) {
        this.id = id;
    }

    public String getValue() {
        return Value;
    }

    public void setValue(String value) {
        Value = value;
    }

}

Domain.java

Domain.java

import java.util.List;

import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlElementWrapper;
import javax.xml.bind.annotation.XmlRootElement;

@XmlRootElement(name="domain")
@XmlAccessorType(XmlAccessType.FIELD)
public class Domain {

    @XmlElement
    private String name;

    @XmlElement
    private String schemaVersion;

    @XmlElementWrapper(name = "subdomain")
    @XmlElement(name = "name")
    private List<String> subdomain;

    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public String getSchemaVersion() {
        return schemaVersion;
    }
    public void setSchemaVersion(String schemaVersion) {
        this.schemaVersion = schemaVersion;
    }
    public List<String> getSubdomain() {
        return subdomain;
    }
    public void setSubdomain(List<String> subdomain) {
        this.subdomain = subdomain;
    }

    public Domain() {

    }

    public Domain(String name, String schemaVersion, List<String> subdomain) {
        this.name = name;
        this.schemaVersion = schemaVersion;
        this.subdomain = subdomain;
    }
}

Employee.java

Employee.java

import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlRootElement;

import org.apache.camel.dataformat.bindy.annotation.CsvRecord;
import org.apache.camel.dataformat.bindy.annotation.DataField;


@XmlRootElement(namespace = Constants.EMPLOYEE_NAMESPACE)
@XmlAccessorType(XmlAccessType.FIELD)
@CsvRecord(separator = ",")
public class Employee {

    @DataField(pos = 1)
    @XmlElement
    private String employeeId;

    @DataField(pos = 2)
    @XmlElement
    private String lanId;

    @DataField(pos = 3)
    @XmlElement
    private String status;

    @DataField(pos = 4)
    @XmlElement
    private String costCentre;

    @DataField(pos = 5)
    @XmlElement
    private String groupName;

    @DataField(pos = 6)
    @XmlElement
    private String divisionName;

    @DataField(pos = 7)
    @XmlElement
    private String departmentName;

    @DataField(pos = 8)
    @XmlElement
    private String region;

    @DataField(pos = 9)
    @XmlElement
    private String businessTitle;

    @DataField(pos = 10)
    @XmlElement
    private String officeLocation;

    @DataField(pos = 11)
    @XmlElement
    private String legalEntity;

    @DataField(pos = 12)
    @XmlElement
    private String birthDate;

    @XmlElement
    private Name name;

    public String getEmployeeId() {
        return employeeId;
    }

    public void setEmployeeId(String employeeId) {
        this.employeeId = employeeId;
    }

    public String getLanId() {
        return lanId;
    }

    public void setLanId(String lanId) {
        this.lanId = lanId;
    }

    public String getStatus() {
        return status;
    }

    public void setStatus(String status) {
        this.status = status;
    }

    public String getCostCentre() {
        return costCentre;
    }

    public void setCostCentre(String costCentre) {
        this.costCentre = costCentre;
    }

    public String getGroupName() {
        return groupName;
    }

    public void setGroupName(String groupName) {
        this.groupName = groupName;
    }

    public String getDivisionName() {
        return divisionName;
    }

    public void setDivisionName(String divisionName) {
        this.divisionName = divisionName;
    }

    public String getDepartmentName() {
        return departmentName;
    }

    public void setDepartmentName(String departmentName) {
        this.departmentName = departmentName;
    }

    public String getRegion() {
        return region;
    }

    public void setRegion(String region) {
        this.region = region;
    }

    public String getBusinessTitle() {
        return businessTitle;
    }

    public void setBusinessTitle(String businessTitle) {
        this.businessTitle = businessTitle;
    }

    public String getOfficeLocation() {
        return officeLocation;
    }

    public void setOfficeLocation(String officeLocation) {
        this.officeLocation = officeLocation;
    }

    public String getLegalEntity() {
        return legalEntity;
    }

    public void setLegalEntity(String legalEntity) {
        this.legalEntity = legalEntity;
    }

    public String getBirthDate() {
        return birthDate;
    }

    public void setBirthDate(String birthDate) {
        this.birthDate = birthDate;
    }

    public Name getName() {
        return name;
    }

    public void setName(Name name) {
        this.name = name;
    }
}

SourceAuditRecord.java

SourceAuditRecord.java

import java.util.List;

import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlRootElement;

@XmlRootElement(name="sourceAuditRecord")
@XmlAccessorType(XmlAccessType.FIELD)
public class SourceAuditRecord {

    @XmlElement
    private String system;

    @XmlElement
    private List<Timestamp> timestamp;

    public String getSystem() {
        return system;
    }

    public void setSystem(String system) {
        this.system = system;
    }

    public List<Timestamp> getTimestamp() {
        return timestamp;
    }

    public void setTimestamp(List<Timestamp> timestamp) {
        this.timestamp = timestamp;
    }
}

Timestamp.java

Timestamp.java

import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlRootElement;

@XmlRootElement(name="timestamp")
@XmlAccessorType(XmlAccessType.FIELD)
public class Timestamp {

    @XmlElement
    private String created;

    @XmlElement
    private String updated;

    @XmlElement
    private String sent;

    public String getCreated() {
        return created;
    }

    public void setCreated(String created) {
        this.created = created;
    }

    public String getUpdated() {
        return updated;
    }

    public void setUpdated(String updated) {
        this.updated = updated;
    }

    public String getSent() {
        return sent;
    }

    public void setSent(String sent) {
        this.sent = sent;
    }
}

Trailer.java

Trailer.java

import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlAttribute;
import javax.xml.bind.annotation.XmlRootElement;
import javax.xml.bind.annotation.XmlValue;

@XmlRootElement(name = "checksum")
@XmlAccessorType(XmlAccessType.FIELD)
public class Trailer {

    @XmlAttribute(name = "type")
    private String checksum;

    @XmlValue
    private String Value;

    public String getChecksum() {
        return checksum;
    }

    public void setChecksum(String checksum) {
        this.checksum = checksum;
    }


    public String getValue() {
        return Value;
    }

    public void setValue(String value) {
        Value = value;
    }
}

Constants.java

Constants.java

public class Constants {

    public static final String SHA_256_HEX = "sha-256";
    public static final String HRCANONICAL_XML = "app/hrci-files/SampleEmployee_V1_20201103.xml";
    public static final String EMPLOYEE_NAMESPACE = "http://www.xxxx/party/employee/v1";
    public static final String HEADER_DOMAIN_NAME = "party";
    public static final String HEADER_SCHEMA_VERSION = "1.0";
    public static final String HEADER_SUB_DOMAIN_NAME = "employee";
    public static final String HEADER_DOMAIN_SOURCE = "HRSystem";
    public static final String HEADER_DOMAIN_MESSAGEID = "HRSystem";
    public static final String HEADER_IDENTIFIER = "HRSystem/lanId";

}

Service.java

Service.java

public void process(Employee employee) {
        logger.info("process employee data started");

        CanonMessage message = new CanonMessage();

        // Set Header Record start
        String correlationId = java.util.UUID.randomUUID().toString();

        logger.info("correlationId - "+correlationId);

        List<String> subDomainNameList = new ArrayList<>();
        subDomainNameList.add(Constants.HEADER_SUB_DOMAIN_NAME);
        Domain domain = new Domain(Constants.HEADER_DOMAIN_NAME, Constants.HEADER_SCHEMA_VERSION, subDomainNameList);
        List<Domain> domainList = new ArrayList<>();
        domainList.add(domain);
        Identifier identifier = new Identifier();
        List<Identifier> identifierList = new ArrayList<>();
        identifier.setId(Constants.HEADER_IDENTIFIER);
        identifier.setValue(employee.getLanId());
        identifierList.add(identifier);
        Header header = new Header(domainList, identifierList, Constants.HEADER_DOMAIN_SOURCE, correlationId);
        List<Header> headerList = new ArrayList<>();
        headerList.add(header);
        message.setMetadata(headerList);
        // Set Header Record end

        // Set Employee Payload Record start
        List<Employee> employeeList = new ArrayList<>();
        employeeList.add(employee);
        message.setEmployee(employeeList);
        // Set Employee Payload Record start

        // Set Audit Record start
        SourceAuditRecord sourceAuditRecord = new SourceAuditRecord();
        sourceAuditRecord.setSystem(Constants.HEADER_DOMAIN_SOURCE);

        Timestamp timestamp = new Timestamp();
        DateTimeFormatter myFormatObj = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
        LocalDateTime myDateObj = LocalDateTime.now();
        String currentDateString = myDateObj.format(myFormatObj);
        logger.info("currentDateString - "+currentDateString);
        timestamp.setCreated(currentDateString);
        timestamp.setSent(currentDateString);
        List<Timestamp> timestampList = new ArrayList<>();
        timestampList.add(timestamp);

        sourceAuditRecord.setTimestamp(timestampList);

        List<SourceAuditRecord> sourceAuditRecordList = new ArrayList<>();
        sourceAuditRecordList.add(sourceAuditRecord);

        message.setSourceAuditRecord(sourceAuditRecordList);
        // Set Audit Record end

        // Set Trailer Record start
        String sha256hex = DigestUtils.sha256Hex(Constants.SHA_256_HEX);
        logger.info("sha256hex - "+sha256hex);

        List<Trailer> trailerList = new ArrayList<>();
        Trailer trailer = new Trailer();
        trailer.setChecksum(Constants.SHA_256_HEX);
        trailer.setValue(sha256hex);
        trailerList.add(trailer);

        message.setTrailer(trailerList);

        JAXBContext context;
        try {
            context = JAXBContext.newInstance(CanonMessage.class);
            Marshaller jaxbMarshaller = context.createMarshaller();
            jaxbMarshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, Boolean.TRUE);

            // Write to System.out
            jaxbMarshaller.marshal(message, System.out);

            StringBuffer sb = new StringBuffer();
            StringWriter sw = new StringWriter();
            jaxbMarshaller.marshal(message, sw);
            String xmlString = sw.toString();
            System.out.println("xmlString - "+xmlString);

            SolaceTopicPublisher publisher = new SolaceTopicPublisher();
            publisher.publishMessage(xmlString);

            logger.info("Employee data processed");
        } catch (JAXBException e) {
            logger.error("Error occurred while creating XML data : "+e.getMessage());
            e.printStackTrace();
        }

    }

这篇关于如何使用JAXB在Java中将CSV转换为XML的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!

08-29 02:26